CORS Configuration
Configure Cross-Origin Resource Sharing (CORS) for HelloJohn — allowed origins, preflight requests, and common setups.
CORS Configuration
HelloJohn's API enforces CORS to ensure only authorized origins can make cross-origin requests from browsers. This page explains how CORS works in HelloJohn and how to configure it correctly.
How HelloJohn Handles CORS
HelloJohn enforces a whitelist of allowed origins per tenant. Only requests from listed origins receive the Access-Control-Allow-Origin header. Unlisted origins receive a CORS error in the browser.
Your backend-to-HelloJohn requests (server-side) are not subject to CORS — CORS only applies to browser-based requests.
Configuring Allowed Origins
Via Dashboard
Go to Tenant Settings → Security → Allowed Origins and add your application domains.
Via Admin API
curl -X PATCH "https://api.hellojohn.dev/v1/admin/config" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"allowed_origins": [
"https://app.example.com",
"https://www.example.com",
"https://staging.example.com"
]
}'Rules:
- Must include the full scheme (
https://) - No trailing slashes
- Port must be included if non-standard (e.g.,
http://localhost:3000) - Wildcards (
*) are not supported — list each origin explicitly
Local Development
Add http://localhost:3000 (or your dev port) to allowed origins during development:
curl -X PATCH "https://api.hellojohn.dev/v1/admin/config" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"allowed_origins": [
"https://app.example.com",
"http://localhost:3000",
"http://localhost:5173"
]
}'Remove localhost from production tenant configuration. Keep a separate tenant for local development.
CORS Headers Returned by HelloJohn
For requests from allowed origins:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Tenant-ID
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400Access-Control-Allow-Credentials: true is required for cookies (refresh tokens). This is why wildcard origins (*) cannot be used — browsers reject credentials with *.
Preflight Requests
Browsers send an OPTIONS preflight request before non-simple requests. HelloJohn handles preflight automatically — no configuration needed on your side.
If you see preflight requests failing, check:
- The origin is in your allowed origins list
- The
X-Tenant-IDheader is listed inAccess-Control-Allow-Headers(it is by default) - You're using
https://— HTTP origins may be blocked depending on configuration
Backend Proxy Pattern
The most secure pattern routes all HelloJohn calls through your own backend, eliminating browser CORS entirely:
Browser → Your Backend → HelloJohn API// Your backend (Express)
app.post("/auth/sign-in", async (req, res) => {
// Proxy to HelloJohn — no CORS issues (server-to-server)
const hjRes = await fetch("https://api.hellojohn.dev/v1/auth/sign-in", {
method: "POST",
headers: {
"X-Tenant-ID": process.env.HELLOJOHN_TENANT_ID!,
"Content-Type": "application/json",
},
body: JSON.stringify(req.body),
});
const data = await hjRes.json();
// Set refresh token in HttpOnly cookie (browser can't read it)
res.cookie("hj_refresh_token", data.refresh_token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/auth/refresh",
});
res.json({ access_token: data.access_token });
});Benefits:
- No CORS configuration needed on HelloJohn
- Refresh token never exposed to browser JavaScript
- Your backend can add extra validation logic
Common CORS Errors
No 'Access-Control-Allow-Origin' header
The origin making the request is not in the allowed origins list.
Fix: Add the origin to your tenant's allowed origins via the dashboard or API.
CORS error on localhost
Your local development origin is not in the list.
Fix: Add http://localhost:{port} to allowed origins.
Credentials flag is set but no 'Access-Control-Allow-Credentials'
This shouldn't happen with HelloJohn (credentials are always allowed for whitelisted origins), but can occur if you're using a self-hosted instance with misconfigured proxy headers.
Fix: Ensure your reverse proxy forwards the Origin header to HelloJohn:
proxy_set_header Origin $http_origin;Request header not allowed
A custom header in your request is not in the CORS Access-Control-Allow-Headers list.
Fix: If you're adding custom headers to HelloJohn API requests, open a support request to have them added.
Self-Hosted CORS Configuration
For self-hosted HelloJohn, configure allowed origins in environment variables:
HELLOJOHN_ALLOWED_ORIGINS=https://app.example.com,https://www.example.com,http://localhost:3000Or in your reverse proxy (nginx):
map $http_origin $cors_origin {
default "";
"https://app.example.com" $http_origin;
"https://www.example.com" $http_origin;
"http://localhost:3000" $http_origin;
}
server {
location /v1/ {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Tenant-ID" always;
if ($request_method = OPTIONS) {
add_header Access-Control-Max-Age 86400;
return 204;
}
proxy_pass http://hellojohn:8080;
}
}