Zero-Server Contact Forms With AWS Lambda and SES
How to build a production-ready contact form backend using AWS Lambda, API Gateway, and SES — no server, no framework, essentially zero cost.
Every marketing site needs a contact form. The options are usually: a paid third-party service, a contact form plugin with its own backend, or rolling your own endpoint. AWS Lambda plus SES is the “roll your own” option done correctly — zero server maintenance, sub-millisecond cold start, and effectively free at any reasonable volume.
The Architecture
API Gateway provides the HTTP endpoint. Lambda handles the form submission and validation. SES delivers the email to your inbox.
Total infrastructure: three AWS services, one Node.js function under 100 lines.
The Lambda Function
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
const ses = new SESClient({ region: "us-east-1" });
export const handler = async (event) => {
const headers = {
"Access-Control-Allow-Origin": "https://yourdomain.com",
"Content-Type": "application/json",
};
if (event.requestContext?.http?.method === "OPTIONS") {
return { statusCode: 200, headers, body: "" };
}
const { name, email, message } = JSON.parse(event.body || "{}");
if (!name || !email || !message) {
return {
statusCode: 400,
headers,
body: JSON.stringify({ error: "All fields required" }),
};
}
await ses.send(new SendEmailCommand({
Source: "[email protected]",
Destination: { ToAddresses: ["[email protected]"] },
Message: {
Subject: { Data: `Contact: ${name}` },
Body: {
Text: { Data: `From: ${name} <${email}>\n\n${message}` },
},
},
}));
return {
statusCode: 200,
headers,
body: JSON.stringify({ success: true }),
};
};
IAM Permissions
The Lambda execution role needs exactly one custom permission: ses:SendEmail. Use the principle of least privilege — scope it to your verified SES identity if you want to be rigorous.
SES Sandbox Considerations
New AWS accounts start in SES sandbox mode. You can only send to verified email addresses. For a contact form where you’re always sending to yourself, this is fine and requires no action.
If you ever need to send to arbitrary addresses (newsletters, transactional emails), request SES production access through the AWS console. The review process takes 24-48 hours.
API Gateway Configuration
Use an HTTP API (not REST API) — it’s simpler, cheaper, and faster. Enable CORS in the API configuration, set the integration to your Lambda function, and deploy a prod stage.
The total monthly cost at typical contact form volumes: zero. Lambda’s free tier is 1 million requests per month. You’d need a very successful marketing site to exceed it.