[785] fix: resolve 403 Forbidden error on form submission in production - #212
Conversation
VitalyyP
commented
Aug 12, 2025
- Add CSRF token to form widget template
- Add CORS configuration for production environment
- Add CSRF token to form widget template - Add CORS configuration for production environment - Ensure proper session and CSRF handling for production deployment
WalkthroughAdded a CORS configuration block to the @apostrophecms/express module in website/app.js (origin set to https://speedandfunction.com in production, otherwise allow all; credentials enabled; methods: GET, POST, PUT, DELETE, OPTIONS; headers: Content-Type, Authorization, X-Requested-With, X-CSRF-Token). Added a hidden CSRF input to website/modules/@apostrophecms/form-widget/views/widget.html to submit apos.csrfToken with form submissions. No public API or signature changes. Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these settings in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
🔍 Vulnerabilities of
|
| digest | sha256:5b5c49de633641f644c9b03f79b750c3481a9cb7eb5c537bc15a59de44221be5 |
| vulnerabilities | |
| platform | linux/amd64 |
| size | 291 MB |
| packages | 984 |
📦 Base Image node:23-alpine
| also known as |
|
| digest | sha256:b9d38d589853406ff0d4364f21969840c3e0397087643aef8eede40edbb6c7cd |
| vulnerabilities |
Description
| ||||||||||||
Description
| ||||||||||||
Description
| ||||||||||||
Description
| ||||||||||||
Description
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
website/app.js (2)
35-51: CORS block is on the right track; tighten prod origins, align with BASE_URL, and include common CSRF header variants
- In production, consider allowing both apex and www subdomain to avoid broken submissions if traffic hits www.
- Prefer deriving allowed origins from BASE_URL to avoid hard-coding.
- Narrow methods to what you actually need (likely GET, POST, OPTIONS for the form submit) and add maxAge to reduce preflight chatter.
- Include common CSRF header variants that csurf accepts (x-xsrf-token, csrf-token) to avoid surprises with different clients.
Apply this diff within the CORS block:
- // Add CORS configuration for production - cors: { - origin: (() => { - if (process.env.NODE_ENV === 'production') { - return 'https://speedandfunction.com'; - } - return true; - })(), - credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: [ - 'Content-Type', - 'Authorization', - 'X-Requested-With', - 'X-CSRF-Token', - ], - }, + // CORS configuration (restrict origins in production) + cors: { + origin: (() => { + if (process.env.NODE_ENV === 'production') { + const base = getEnv('BASE_URL'); + // Allow apex and www variants of BASE_URL in prod + const u = new URL(base); + const apex = `${u.protocol}//${u.hostname}`; + const www = u.hostname.startsWith('www.') + ? `${u.protocol}//${u.hostname.replace(/^www\./, '')}` + : `${u.protocol}//www.${u.hostname}`; + return [apex, www]; + } + // Reflect request origin in non-prod + return true; + })(), + credentials: true, + methods: ['GET', 'POST', 'OPTIONS'], + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'X-Requested-With', + 'X-CSRF-Token', + 'X-XSRF-Token', + 'CSRF-Token' + ], + // Cache preflight for a day + maxAge: 86400 + },
35-35: Nit: comment implies “for production” but config applies to all envsThe block is active in all environments (prod restricts origin, non-prod reflects any). Consider updating the comment for clarity.
- // Add CORS configuration for production + // CORS configuration (prod: restricted origins; non-prod: reflect any origin)website/modules/@apostrophecms/form-widget/views/widget.html (1)
23-23: Optional: guard for missing token to avoid empty submission in edge casesIf CSRF is ever disabled or not injected on a route, this would emit an empty hidden field. It won’t break, but we can conditionally render it.
- <input type="hidden" name="_csrf" value="{{ apos.csrfToken }}" /> + {% if apos.csrfToken %} + <input type="hidden" name="_csrf" value="{{ apos.csrfToken }}" /> + {% endif %}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
website/app.js(1 hunks)website/modules/@apostrophecms/form-widget/views/widget.html(1 hunks)
🔇 Additional comments (2)
website/app.js (1)
25-34: SameSite 'lax' is appropriate – no external form hosts detectedWe searched for any HTML forms posting to off-site domains and found none. All client-side
fetchcalls either use relative URLs (e.g.form.action) or hit known server-side endpoints (infinite scroll, reCAPTCHA). Therefore, keepingsameSite: 'lax', secure: process.env.NODE_ENV === 'production',is correct for your current topology.
website/modules/@apostrophecms/form-widget/views/widget.html (1)
23-23: Good fix: hidden CSRF field matches server expectationsUsing
<input type="hidden" name="_csrf" value="{{ apos.csrfToken }}" />aligns with the csurf defaults and with your cookie key. This should address the 403s from missing token on POST.
|
@VitalyyP could you rebase the branch against main to be merged without conflicts? |
|
|
LGTM |


