How to Fix AWS API Gateway Custom Domain 403 Error (Base Path & Stage Routing)
If your API Gateway custom domain throws a 403 the moment you hit it — while the ugly default execute-api URL works perfectly — the cause almost always sits in one small, easy-to-miss setting: the base path mapping. That's the object that tells API Gateway which API and which stage a custom domain URL should actually route to, and when it's missing, wrong, or duplicated in the URL, you get a 403 instead of a helpful error message. Fix the mapping, and in the vast majority of cases the "permissions problem" you were chasing disappears, because it was never a permissions problem at all. If your error was due to 429, check this post API Gateway: 429 throttled - account vs stage limits which was different problem.
Jake found this out the expensive way. He'd wired a booking API behind bookings.jakesphonefix.com so customers could reserve a repair slot from their phones, and it worked great in testing on the raw execute-api URL. The morning he switched the booking widget on his site over to the custom domain, every single booking attempt came back with a blank error page. Three walk-ins later, he figured out something was wrong and just started writing appointments on paper again.
"I checked everything," he told Ethan that afternoon. "The Lambda function works. The IAM role works. I even re-checked the SSL certificate twice. Why would AWS just... refuse me?"
Why a Custom Domain Returns 403 When the Rest of the API Works Fine
"Here's the part that trips almost everybody up," Ethan said. "A 403 from API Gateway sounds like a permissions error — like someone locked a door on you. Most of the time, with a custom domain, it's not that at all. It's a routing problem wearing a permissions costume."
Amazon API Gateway's own troubleshooting documentation backs this up directly: when you invoke a custom domain and there's no base path mapped to an API, API Gateway returns a 403 with the error type ForbiddenException and the message body simply says "Forbidden." Separately, if your request URL still includes the stage name after the custom domain — something that feels like the responsible, explicit thing to do — API Gateway returns a 403 with error type MissingAuthenticationTokenException, even though nothing about authentication is actually missing. Both of these look, at a glance, exactly like an access-denied error. Neither one is.
⚡ MUST-READ AWS DOMAIN & ROUTING GUIDES
- π AWS Route 53 Hosted Zone & DNS Architecture Explained
- π AWS Serverless Architecture: Pros, Cons & Hidden Custom Domain Limits
- π Fix API 403 Forbidden: Amazon Cognito Auth & Custom Domain Mapping
- ⚡ Reduce API Gateway Latency & Fix Lambda Cold Starts
- π️ Automate Custom Domains & Base Path Mapping with AWS IaC
That's the counterintuitive part worth sitting with: the "safe" instinct — typing the stage name into the URL because that's what worked on the execute-api endpoint — is one of the single most common causes of this exact 403. The custom domain hides the stage on purpose. Putting it back in breaks the very thing the mapping was built to simplify.
♂️ Jake's Reality Check
"So the domain is basically lying to me about the URL structure? I thought bookings.logeshwaran.org/prod/book was the correct, careful version."
It's the opposite. Once a base path mapping connects your custom domain to a stage, that stage is baked into the mapping itself. Typing the stage into the URL on top of that sends API Gateway looking for a resource path called /prod/book that doesn't exist — because the real resource is just /book. The domain isn't lying; it already did the stage part for you.
What a Base Path Mapping Actually Does
A quick vocabulary stop, because nothing in this post should send you off to another tab. When you create an API in API Gateway, AWS gives it an ugly, auto-generated hostname like a1b2c3d4.execute-api.us-east-1.amazonaws.com. That's called the execute-api endpoint, and it's tied to a "stage" — a named, deployed snapshot of your API, like prod, dev, or v1. The full working URL always looks like https://a1b2c3d4.execute-api.us-east-1.amazonaws.com/prod/book.
A custom domain name is a friendlier hostname you register, like api.jakesphonefix.com, that sits in front of one or more of those execute-api endpoints. But a hostname alone doesn't know which API or which stage to send traffic to. That connection is the job of an API mapping (called a "base path mapping" for REST APIs specifically) — a small record that says, in effect: "requests to this custom domain, optionally under this base path, go to this specific API and this specific stage." Without that record, or with the wrong one, the custom domain is just a hostname pointed at nothing useful, and API Gateway responds with a 403 rather than routing the request anywhere.
Think of the custom domain as the storefront sign and the base path mapping as the note taped to the inside of the door telling the mail carrier which suite number to actually deliver to. The sign out front can be perfect. If that note is missing, wrong, or torn, the mail still doesn't get where it needs to go — it just gets refused at the door instead.
Check Your Current Base Path Mapping First
Before changing anything, look at what's actually configured. This takes thirty seconds and tells you immediately whether you're dealing with a missing mapping, a wrong one, or something further downstream.
Ethan calls this the "thirty-second tax" — the same habit that makes Jake unplug his shop's router every single Monday morning before he even looks at why the card reader is being slow that day. Nine times out of ten it's the router. With a custom domain 403, nine times out of ten it's the mapping. Check the boring, cheap thing before the expensive one.
- Open the console path first. In the API Gateway console, choose Custom domain names in the left navigation, select your domain, and open the API mappings tab. This shows every API, stage, and path currently mapped to that domain in one screen.
- Or pull it with the CLI. Run
aws apigateway get-base-path-mappings --domain-name your-domain.comfor a REST API. The response lists every mapping as abasePath, arestApiId, and astage. A base path of(none)means requests hit the domain root directly with no extra path segment required. - Match it against the URL you're actually calling. If the mapping shows
basePath: (none),stage: prod, then the correct call ishttps://your-domain.com/book— neverhttps://your-domain.com/prod/book. If the mapping showsbasePath: orders, the correct call ishttps://your-domain.com/orders/book, and the stage name never appears in the URL at all, regardless of what it's called. - If the command returns an empty
itemslist, there is no mapping at all — that alone explains a 403 on every request, no matter what the resource itself looks like.
This single check resolves the majority of custom-domain 403 tickets. It's also the fastest way to rule the mapping in or out before you go chasing IAM policies, WAF rules, or certificate issues that have nothing to do with your actual problem.
Fix: No Base Path Mapping Exists at All
This is the plain, boring version of the problem, and it's more common than people expect — usually because the custom domain was created in one step of a setup guide and the mapping step was skipped, forgotten, or failed silently.
To create one for a REST API through the console: open Custom domain names, choose your domain, open API mappings, choose Configure API mappings, then Add new mapping, and fill in the API, the stage, and — only if you want one — a path. Leave the path blank if you want the domain root itself to route directly to the stage.
Through the CLI, for a REST API mapped with a single-level path:
aws apigateway create-base-path-mapping \
--domain-name your-domain.com \
--rest-api-id a1b2c3d4 \
--stage prod \
--base-path orders
Leave off --base-path entirely (or pass (none)) if you want requests to the bare domain root to go straight to that stage with no extra path segment. That's the setup Jake actually wanted: bookings.jakesphonefix.com/book, not bookings.jakesphonefix.com/orders/book.
✅ Why this is the one to use
Mapping with (none) as the base path is the cleanest option whenever a custom domain is dedicated to a single API. It keeps URLs short, matches what most front-end code expects, and avoids the single most common mistake in this whole troubleshooting chain — repeating the stage name in the URL after the domain already encodes it.
Fix: The Mapping Points to the Wrong API or Stage
A second, quieter version of this problem: a mapping does exist, but it points at the wrong API ID, or the wrong stage on the right API. This usually shows up after a redeploy to a new stage name, after an API was recreated with a fresh ID (which happens more often than people expect — deleting and recreating an API doesn't preserve the old ID), or when a mapping was set up in a hurry against a dev stage and nobody moved it to prod before launch.
The symptom looks identical to a missing mapping from the outside: 403, "Forbidden." The difference only shows up once you actually run get-base-path-mappings and compare the restApiId and stage fields in the response against the API you meant to expose.
⚠️ What this actually breaks
You can't simply create a second mapping with the same base path pointing to the new API — a base path has to be unique across all mappings on a single domain. You either have to delete the old mapping first (aws apigateway delete-base-path-mapping) and recreate it against the correct API and stage, or update it in place with update-base-path-mapping. If you delete it and something else references that mapping — a health check, a monitoring script, a hardcoded integration test — that call goes dark until the new mapping is live.
REST APIs vs. HTTP APIs: Why the Fix Looks Slightly Different
API Gateway offers two different API types — REST APIs (the original, more feature-rich type) and HTTP APIs (a newer, leaner, cheaper type). Both use the same underlying idea of an "API mapping," but the tooling and some of the rules differ slightly, and mixing up which command you're supposed to run is its own source of confusion.
| Detail | REST API | HTTP API |
|---|---|---|
| CLI object name | Base path mapping (aws apigateway ...) |
API mapping (aws apigatewayv2 ...) |
Multi-level base paths (e.g. v1/orders) |
Requires the v2 API even for a REST API, and a Regional domain on the TLS 1.2 security policy | Supported the same way, same requirement |
| Execution logging for troubleshooting | Supported — CloudWatch access logs show whether requests even reach the API | Not supported for HTTP APIs, which makes a fast test mapping to a REST API for isolation more valuable |
| Sharing one custom domain across types | A Regional custom domain can map both REST and HTTP APIs together | Same domain, different base paths, works fine |
"That logging gap is worth remembering," Ethan added. "HTTP APIs don't support execution logging the way REST APIs do, so when a custom domain sits in front of an HTTP API and you're stuck, one legitimate diagnostic trick is to create a throwaway mapping on the same domain pointing at a REST API instead — even a bare-bones test API — just so you can see log events in CloudWatch and confirm whether requests are reaching API Gateway at all before you reroute the mapping back."
Edge-Optimized vs. Regional Domains, and Certificate Mismatches
Custom domains come in two flavors. An edge-optimized domain routes through a CloudFront distribution and is meant for clients spread across the globe; it needs its certificate in the US East (N. Virginia) Region no matter where your API actually lives. A Regional domain talks directly to your API Gateway endpoint in its own AWS Region, needs its certificate stored in that same Region, and is the only option if you want mutual TLS or multi-level base paths.
Getting the certificate Region wrong doesn't usually throw a 403 by itself — it tends to fail earlier, during domain creation or validation — but it's worth ruling out early because a broken domain setup and a broken base path mapping can look similar from the outside ("nothing works") even though the fixes are completely different. If your domain itself won't validate or won't finish creating, that's a certificate and DNS problem, not a base path mapping problem, and no amount of remapping will fix it.
One more edge-type quirk worth knowing: because an edge-optimized custom domain sits behind CloudFront, a change to the base path mapping on that domain type can take longer to be visible everywhere than the same change on a Regional domain, since it has to propagate through the distribution. If you just fixed the mapping and it still 403s for a few minutes afterward on an edge-optimized domain, that's a reasonable thing to wait out before assuming the fix didn't take.
When It's Not the Mapping at All: A Symptom Triage Table
Here's the honest part most articles skip: sometimes the base path mapping is completely correct and you're still getting a 403. API Gateway happens to return the exact same status code for a whole family of unrelated problems, and the only reliable way to tell them apart is the error type and message in the response body. This table is built directly from API Gateway's own error-cause documentation.
| What you see | Error type in response | Real cause |
|---|---|---|
| Custom domain, no path mapped | ForbiddenException | No base path mapping exists for that domain and path |
| Stage name typed into the custom domain URL | MissingAuthenticationTokenException | The mapping already encodes the stage; adding it again points at a resource path that doesn't exist |
| "User: anonymous is not authorized to perform: execute-api:Invoke" | AccessDeniedException | A resource policy attached to the API doesn't explicitly allow this caller, or explicitly denies it |
| "Forbidden" with no other detail, and AWS WAF is attached | ForbiddenException | A WAF rule is blocking the request before it reaches the API's own logic |
| "Forbidden" only on the custom domain with mTLS turned on | ForbiddenException | The client certificate's issuer isn't in the domain's configured truststore, or the certificate has expired |
| Works on the custom domain, fails on execute-api after you "locked it down" | ForbiddenException | You disabled the default execute-api endpoint, which is expected — only the custom domain works now |
| Private API, called from inside a VPC | ForbiddenException | The private custom domain isn't associated with the VPC endpoint, or you're using public DNS instead of the endpoint's DNS |
| Custom domain uses routing rules | ForbiddenException | No routing rule matches the request, so nothing catches it |
Notice how many rows land on the exact same "Forbidden" message. That's the trap: the response body genuinely does not tell you which of these you're dealing with unless you look at the x-amzn-errortype response header alongside it. Skipping that header and guessing is how people end up rewriting IAM policies for an hour to fix what was actually a missing base path mapping the whole time — or the reverse, remapping paths for an hour when a WAF rule was the real blocker.
♂️ Jake's Reality Check
"I don't even know where to see that error-type header. I've just been staring at 'Forbidden' in the browser."
Use curl, not the browser. Run curl -v https://your-domain.com/book from a terminal and read the response headers it prints. The status line and the x-amzn-errortype header together tell you which row of that table you're actually in, in about two seconds.
Third-Party Tools and Alternatives: When They Help, When to Skip Them
Once people realize the AWS console and CLI can feel clunky for this, the next instinct is often to reach for a third-party tool. Some genuinely help. Others are solving a problem you don't have.
| Tool | What it actually helps with | Skip it if... |
|---|---|---|
| Postman / Insomnia | Seeing full response headers (including x-amzn-errortype) without wrestling with curl syntax |
You're already comfortable reading curl's verbose output — it tells you the same thing |
| Terraform / Serverless Framework / AWS SAM | Defining the base path mapping as part of the same deploy as the API, so it can't drift out of sync | You just need to fix one existing mapping right now — reach for the CLI first, migrate to code afterward |
| CloudWatch Synthetics canaries | Catching a broken mapping automatically on a schedule, before a customer does | You're troubleshooting an active incident right now — set this up after, not during |
| Third-party API gateway or proxy wrappers | Genuinely useful for teams standardizing multi-cloud API management | You're only trying to resolve one 403 — this adds a whole new layer to debug, not a fix |
"The honest advice here," Ethan said, "is that nobody needs a new tool to fix a base path mapping. The native CLI does the entire job in two commands. Reach for Terraform or a canary afterward, to make sure you never have to do this fix twice — not instead of doing the fix."
Step-by-Step: Fixing a Custom Domain 403 From Scratch
Put together, here's the order that wastes the least time, cheapest checks first:
- Confirm the execute-api URL works. Call
https://your-api-id.execute-api.your-region.amazonaws.com/your-stage/your-resourcedirectly. If this also fails, your problem isn't the custom domain at all — go fix the API itself first. - Run
get-base-path-mappingsagainst your domain and write down everybasePath,restApiId, andstageit returns. - Build the correct custom domain URL from that output: domain, plus base path (skip it entirely if the mapping shows
(none)), plus resource path — and never the stage name. - If no mapping exists, create one with
create-base-path-mapping(REST API) orcreate-api-mapping(HTTP API, or any REST API mapping that needs multiple path levels). - If a mapping exists but points at the wrong API or stage, update it with
update-base-path-mapping, or delete and recreate it, being aware that anything already calling the old mapping goes dark during the gap. - Re-test with curl, not a browser, and read the
x-amzn-errortypeheader if you still get a 403. - If the error type isn't about a missing mapping or a stage in the URL, walk the symptom table above — resource policy, WAF, mutual TLS, private API, disabled default endpoint, or routing rules — instead of touching the mapping again.
- Turn on CloudWatch access logging for a REST API if you're still stuck, so you can see whether the request is even reaching API Gateway before it fails, versus failing somewhere else entirely (a CDN, a firewall, a corporate proxy).
Every step here is deliberately cheap before it's drastic. Nobody needs to touch an IAM policy, a WAF web ACL, or a certificate before confirming, in under a minute, whether the mapping itself is even the culprit.
Verifying the Fix With curl and CloudWatch Logs
Once you've made a change, don't just refresh the browser tab and hope. A browser hides response headers and often caches failed requests longer than you'd expect, which makes it look like nothing changed even after a genuine fix.
Run curl -X GET -v https://your-domain.com/book and read the whole thing top to bottom: the DNS resolution, the TLS handshake, the request headers actually sent, and the full response including status line and every header. A successful fix shows a 200 (or whatever your integration returns) instead of a 403, and the x-amzn-errortype header simply won't be present anymore.
For REST APIs, turning on execution or access logging in CloudWatch adds a second, independent confirmation: you can watch log events arrive in near-real time as you send test requests, which tells you definitively whether traffic is reaching API Gateway at all — useful when something in front of API Gateway, like a corporate proxy or an old DNS cache entry, is intercepting requests before they ever get there. HTTP APIs don't support this kind of execution logging, which is exactly why mapping a spare REST API to the same domain temporarily, as mentioned earlier, is a legitimate diagnostic move rather than a workaround.
Still Getting 403 After Fixing the Mapping? Here's What's Left
If the base path mapping is provably correct — you've checked it with the CLI, the URL you're calling matches it exactly, and there's still a 403 — the remaining causes cluster into a short list, all documented directly by AWS:
A resource policy is blocking the caller. If your API has a resource policy attached (common when an API is meant to be reachable only from a specific VPC, a specific IP range, or a specific AWS account), and that policy doesn't explicitly allow the caller, or explicitly denies it, you'll see AccessDeniedException with a message naming the caller and the API's resource ARN. Fix by reviewing the resource policy's Principal, Action, and Resource fields for typos or overly narrow scoping — API Gateway does not validate the ARN format when you save a resource policy, so a small mistake there saves silently and fails at request time instead.
AWS WAF is filtering the request. If a web ACL is attached to the API and one of its rules matches your request — a rate limit, a geo-restriction, a managed rule group flagging something in the payload — you get the same generic "Forbidden" response, with no indication in the API Gateway response itself that WAF was involved. The way to confirm this is to check the WAF web ACL's sampled requests or logs, not the API Gateway side at all.
Mutual TLS rejected the client certificate. If the custom domain requires mutual TLS, API Gateway checks the client certificate's issuer against a truststore you configured, and fails the TLS handshake with a 403 if it can't find a match, or if the certificate itself is expired or malformed. This is the one class of 403 in this whole list where the fix genuinely has nothing to do with the base path mapping — it's entirely about the truststore and the certificate.
You disabled the default execute-api endpoint. This one deserves calling out because it's often an intentional setting that later gets mistaken for a bug: once you disable the default endpoint on a REST API, that ugly execute-api URL stops working entirely and only the custom domain works. If you're testing against the execute-api URL out of habit and it suddenly 403s, check whether someone (possibly past-you) turned this off on purpose.
A private custom domain isn't linked to the right VPC endpoint. Private APIs called through a custom domain need that domain associated with the interface VPC endpoint. If you're calling from inside a VPC and get a 403, that association — not the base path mapping — is usually the missing piece, along with making sure private DNS is enabled correctly on the interface endpoint for the DNS name you're actually using.
Multi-Level Base Paths and Other Edge Cases
A single-level base path is something like orders. A multi-level base path is something like v1/orders — two segments deep. AWS documentation is explicit that multi-level base paths require the API Gateway version 2 CLI (apigatewayv2) even for a REST API, and they require a Regional custom domain using the TLS 1.2 security policy specifically — an edge-optimized domain, or a domain on an older security policy, won't support it. If you try to create a multi-level mapping with the classic apigateway commands, it will either fail outright or silently behave differently than you expect.
A domain name is also limited in how many multi-level mappings it can carry: up to 200 multi-level API mappings per domain name, a limit that does not count single-level mappings like a bare prod or orders. Mapping values themselves are restricted to letters, numbers, and a small specific set of characters ($ - _ . + ! * ' ( ) /), and capped at 300 characters — so a base path containing spaces, colons, or other punctuation will simply be rejected rather than silently truncated.
One more subtlety worth knowing before you design a path structure around it: creating multi-level mappings on a REST API through the v2 CLI causes API Gateway to convert all header names in that mapping to lowercase. If any downstream logic is doing case-sensitive header matching, this is the kind of detail that causes a mysterious failure two layers away from where anyone would think to look.
What to Check Before You Hand This Domain to Customers
Once the 403 is gone and the domain works, resist the urge to call it done and move on. A few of the same settings that just caused your outage are worth a second look before real customer traffic — and real customer data — starts flowing through this domain.
Check whether a resource policy is attached and, if so, that it's scoped to what you actually intend — not left wide open from an early testing phase, and not accidentally narrowed to a single office IP that won't matter once customers connect from anywhere. Check whether AWS WAF is attached if this endpoint ever touches payments, account data, or anything else worth protecting from abuse; a mapping fix doesn't add that protection on its own. And if this domain uses mutual TLS, check the truststore for certificates belonging to a partner or vendor relationship that's since ended — an old, still-trusted certificate is a bigger risk sitting quietly than a 403 ever was.
None of this is about the mapping anymore. But since fixing a 403 usually means you're already in these settings anyway, it costs almost nothing to glance at them while you're there.
Preventing This Next Time: Mappings as Code, and a Standing Check
Most of this class of problem exists because a base path mapping is a manual, easy-to-forget click somewhere in a console — a step performed once, months ago, by someone who has since moved teams. The fix for the recurrence, not just the incident, is to stop treating it as a manual step.
Both AWS CloudFormation and infrastructure frameworks built on top of it can define an API mapping as a resource alongside the API and the custom domain itself, so the mapping is created, updated, and torn down in lockstep with everything else rather than existing as a separate, forgettable console action. A CloudFormation snippet defining an AWS::ApiGatewayV2::ApiMapping resource ties a domain name, an API mapping key (the base path), an API ID, and a stage into one declarative block that lives in version control next to the rest of the infrastructure. When someone redeploys to a new stage or recreates the API with a new ID, the mapping updates as part of the same deployment instead of quietly pointing at something that no longer exists.
What actually changes when you move this to code
- Before: a mapping created once by hand, disconnected from the API's own deployment history
- After: a mapping resource in the same template as the API and domain, updated automatically on every deploy
- What that means in practice: an API recreated with a new ID can't silently leave an orphaned mapping behind, because the mapping's reference to the API ID is generated from the same deployment, not typed in by hand months earlier
The adjacent task worth setting up right after this fix, while it's fresh: a standing check that catches a broken mapping before a customer does, instead of after. A short script run as a pre-deploy or post-deploy step — calling get-base-path-mappings and comparing the result against what the deployment expects to have just created — takes only a few lines and fails a build loudly instead of failing a customer's request silently. Layered on top of that, a scheduled CloudWatch Synthetics canary that simply hits the custom domain's real URL on an interval and alarms on anything other than a 200 turns "a customer told us it's broken" into "we already knew and were already fixing it."
"This is the fix Jake actually needed," Ethan said. "Not just correcting the mapping once, but making sure the next person who redeploys that booking API can't accidentally break it the same way again, and that if they do, something notices before a Saturday morning full of walk-ins does."
Frequently Asked Questions
Why does my custom domain return 403 but the execute-api URL works fine?
Because the custom domain and the execute-api endpoint are two entirely separate paths to the same API, connected only by the base path mapping. The execute-api URL always works if the API and stage themselves are healthy, since it doesn't rely on any mapping at all. The custom domain only works once a mapping exists and correctly points at that API and stage — if the mapping is missing or wrong, the custom domain fails while the execute-api URL keeps working, which is exactly the signal that points you at the mapping rather than the API itself.
Do I need to include the stage name in my custom domain URL?
No, and including it is one of the most common causes of this exact 403. A base path mapping already ties your custom domain to a specific stage, so the stage name is never supposed to appear in the custom domain URL. If your mapping has base path (none) and stage prod, the correct URL is your domain plus the resource path directly — never /prod/ in front of it.
What does "(none)" mean in a base path mapping?
It means no extra path segment is required after the domain name at all. A mapping with base path (none) routes requests straight from the domain root to the mapped stage, so https://your-domain.com/book reaches your resource directly rather than needing something like https://your-domain.com/something/book.
Can I map more than one API to the same custom domain?
Yes. A single custom domain can carry multiple base path mappings, each pointing at a different API and stage, as long as each base path value is unique across all the mappings on that domain. This is how one domain like api.example.com can serve /orders from one API and /customers from a completely separate one.
Why does my base path mapping look correct but I still get 403?
Once the mapping itself is confirmed correct, the remaining causes are usually a resource policy that doesn't explicitly allow the caller, an AWS WAF rule blocking the request, an expired or unrecognized mutual TLS client certificate, a private API not linked to the right VPC endpoint, or a disabled default endpoint being called by mistake. Check the x-amzn-errortype response header to identify which of these you're actually dealing with rather than guessing.
Is 403 the same as 404 for API Gateway custom domains?
Functionally, for the most common cause, yes — a missing base path mapping or a wrong stage in the URL is really "this doesn't exist," but API Gateway responds with 403 instead of 404 for these cases. That's worth remembering specifically because it means a 403 on a custom domain shouldn't automatically be treated as a permissions issue the way a 403 usually is elsewhere.
How long does a new base path mapping take to start working?
On a Regional custom domain, changes typically take effect quickly. On an edge-optimized custom domain, the mapping change has to propagate through the underlying CloudFront distribution, which can take longer to be visible everywhere. If a fix still shows 403 immediately after being applied on an edge-optimized domain, waiting a short while before assuming the fix failed is reasonable.
Can I use the same base path across two different custom domains?
Yes. The uniqueness rule applies within a single domain name, not across domains. You can have orders as a base path on api.example.com and also as a base path on partner-api.example.com, each pointing at whatever API and stage makes sense for that domain.
What's the difference between a REST API base path mapping and an HTTP API mapping?
They serve the same purpose — connecting a custom domain to an API and a stage — but REST API mappings use the apigateway CLI commands, while HTTP API mappings and any multi-level REST API mapping use the apigatewayv2 commands instead. HTTP APIs also don't support execution logging, which changes how you'd troubleshoot a 403 that isn't explained by the mapping alone.
Do I need a Regional or Edge-optimized custom domain for multi-level base paths?
Regional. Multi-level base paths — anything with more than one path segment, like v1/orders — require a Regional custom domain name using the TLS 1.2 security policy. An edge-optimized domain doesn't support this, and neither does a Regional domain still on an older security policy.
Why does my API work over HTTPS on execute-api but not on my custom domain?
This is almost always the base path mapping being missing or mismatched, since the execute-api endpoint bypasses the mapping entirely and always routes directly to a stage. If both fail identically, the problem is in the API itself, not the domain. If only the custom domain fails, start with get-base-path-mappings.
Can a resource policy cause a 403 even when the base path mapping is correct?
Yes. A resource policy attached to the API is evaluated independently of the base path mapping. Even with a perfectly correct mapping, a resource policy that doesn't explicitly allow the caller — or that explicitly denies it — returns a 403 with an AccessDeniedException error type and a message naming the caller and the API's resource ARN.
Does AWS WAF show up as the same 403 error as a bad base path mapping?
The status code and the generic "Forbidden" message can look identical, but a request blocked by AWS WAF never reaches the API Gateway routing logic that a base path mapping affects. The only reliable way to tell them apart is checking the WAF web ACL's own logs or sampled requests rather than assuming the API Gateway side is at fault.
My custom domain uses mutual TLS — could that be the real cause of my 403?
Very possibly, and it has nothing to do with the base path mapping. With mutual TLS enabled, API Gateway checks the client certificate's issuer against your configured truststore during the TLS handshake itself, before any request routing happens. An unrecognized issuer or an expired certificate fails the connection with a 403 regardless of how correct the mapping is.
Can I remove the default execute-api endpoint once my custom domain works?
Yes, API Gateway supports disabling the default execute-api endpoint on a REST API once you're confident the custom domain is working correctly. After you do, calls to the execute-api URL itself will start returning 403 by design — that's expected behavior, not a new bug, so don't mistake it for the custom domain problem reappearing.
What's the safest way to test a base path mapping change without breaking production traffic?
Create a new, separate base path mapping — using a throwaway path segment or a completely separate test domain — pointing at the API and stage you actually want to verify, test against that, and only then update or reroute the mapping your production traffic actually uses. This avoids a window where live traffic hits a half-configured or deleted mapping while you're still confirming the fix.
Revision note. Written September 2026, covering current API Gateway REST API and HTTP API base path/API mapping behavior for both edge-optimized and Regional custom domains. It will need a refresh if AWS changes how mapping errors are reported or adds a friendlier error message in place of the generic 403. If you've been staring at a "Forbidden" screen wondering what you did wrong, you probably didn't do anything wrong at all — you just found one of the more oddly-worded corners of how API Gateway reports a routing problem.
π RECOMMENDED AWS TROUBLESHOOTING & ARCHITECTURE