A developer posted this on the WordPress support forums:
"When we deploy to our live instance, the form is not working and gives a JSON error. This path has a 404 not found error:
/wp-json/contact-form-7/v1/contact-forms/978/feedback/schema"
They were using Simply Static to export their WordPress site to static files and deploying the output to a live host. The plugin author's reply pointed to a CORS issue. The developer pushed back. The thread went unresolved for weeks before being closed with "please contact Pro support."
The actual problem is more fundamental than CORS. Contact Form 7 is a dynamic plugin. It processes form submissions via a WordPress REST API endpoint. When you export a WordPress site to static HTML, that REST API does not exist on the static host. You cannot fix this with a CORS header. You need to rearchitect how the form submits.
What Is Actually Happening
When a user fills out a CF7 form and clicks submit, CF7's JavaScript sends a POST request to:
POST /wp-json/contact-form-7/v1/contact-forms/{form_id}/feedback
This endpoint is registered by the CF7 plugin and handled by PHP running on your WordPress server. It validates the submission, sends the email, and returns a JSON response that CF7's JS uses to show the success or error message.
On a static site deployment, there is no PHP. There is no WordPress. There is no REST API. The URL /wp-json/contact-form-7/v1/contact-forms/978/feedback simply does not exist on the static host. The browser gets a 404.
The /feedback/schema 404 the developer saw is CF7 pre-fetching the form's validation schema before submission. That 404 is a symptom, not the root cause. The root cause is that the entire CF7 PHP backend is missing on the static host.
The Three Specific Failures in This Thread
Failure 1: REST API Endpoint Returns 404
/wp-json/contact-form-7/v1/contact-forms/978/feedback/schema
404 Not Found
No PHP runtime on the static host means no WordPress REST API. Every request to /wp-json/ returns 404. This cannot be fixed by configuring Simply Static differently because there is nothing to copy. The endpoint is not a file, it is a dynamic PHP route.
Failure 2: Plugin JS Asset Not Readable
The developer found this in Simply Static's diagnostic output:
/var/www/html/wp-content/plugins/simply-static-pro/assets/ssp-form-webhook-public.js
Status: Not readable
This JavaScript file handles CF7's form submission flow on the static site. Simply Static Pro replaces CF7's default AJAX submission with a webhook-based flow specifically to work around the REST API problem. If this file has incorrect permissions (not world-readable), the static export cannot include it and the form falls back to the broken default behavior.
Fix for this specific issue:
chmod 644 /var/www/html/wp-content/plugins/simply-static-pro/assets/ssp-form-webhook-public.js
Then re-run the Simply Static export.
Failure 3: CORS Error in Browser Console
The plugin author diagnosed a CORS issue. This is partially correct but only as a secondary problem.
If Simply Static Pro is configured to proxy CF7 submissions to a webhook on a different domain from the static site, the browser enforces CORS policy on that cross-origin request. The static site is on static-domain.com, the webhook receiver is on wp-domain.com, the browser blocks the request.
The CORS error in the browser console looks like:
Access to fetch at 'https://wp-domain.com/wp-json/contact-form-7/...'
from origin 'https://static-domain.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
To add CORS headers to your WordPress origin, add this to your .htaccess on the WordPress server (not the static host):
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://static-domain.com"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
</IfModule>
Or programmatically in WordPress:
add_action('rest_api_init', function() {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function($value) {
$origin = get_http_origin();
$allowed = ['https://static-domain.com'];
if (in_array($origin, $allowed)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
}
return $value;
});
});
This only resolves CORS. It does not fix the 404 unless your WordPress installation is still running and accessible at the origin URL.
The Two Architectures for CF7 on Static Sites
There are two patterns that actually work. Everything else is a workaround for a mismatched architecture.
Pattern 1: Keep WordPress Running as the Form Backend
Your static site is served from a CDN or static host. Your WordPress installation stays live at a separate URL (e.g., api.yourdomain.com or a subdomain). CF7 submits to the live WordPress REST API on that subdomain.
Requirements:
- WordPress must remain running and publicly accessible
- CORS headers must be configured on the WordPress origin to allow requests from your static domain
- The form action in the CF7 JS must point to the correct WordPress URL
This is what Simply Static Pro's webhook configuration enables when working correctly.
Pattern 2: Replace CF7 with a Third-Party Form Endpoint
Remove the CF7 REST API dependency entirely. Use a form backend service that accepts POST submissions directly from a static HTML form without requiring a PHP backend.
Options:
- Formspree, Netlify Forms, Getform (managed form endpoints)
- A webhook receiver you control (your own API, n8n, Make)
- Contact Form to API configured to forward submissions to an external endpoint before the static export
If your form needs to reach a CRM or send an email, Contact Form to API can handle that forwarding from within WordPress during the submission event, before Simply Static exports the site. The static site only needs to trigger the submission. The WordPress plugin processes and forwards it while WordPress is still the active runtime.
Diagnostic Steps for CF7 on a Static Site
Step 1: Confirm whether your WordPress backend is still running
Open your browser and go to https://yourwordpressurl.com/wp-json/. If you get a JSON object describing the REST API, WordPress is live. If you get a 404 or your static site's 404 page, the WordPress PHP backend is gone.
Step 2: Check the browser console on form submission
Open DevTools (F12), go to the Console tab, and submit the form. You will see one of:
-
404 Not Foundon the feedback endpoint - WordPress REST API is not reachable -
CORS error- WordPress is reachable but blocking cross-origin requests -
net::ERR_CONNECTION_REFUSED- WordPress server is offline or not accessible
Step 3: Verify the forms.json configuration
Simply Static Pro stores form routing config at:
/wp-content/uploads/simply-static/configs/forms.json
A correctly configured entry looks like:
[{
"id": "978",
"tool": "cf7",
"endpoint": "https://your-wordpress-url.com/?mailme",
"redirect_url": ""
}]
If endpoint points to your static domain instead of your WordPress domain, submissions will never reach the PHP backend.
Step 4: Check JS asset file permissions
ls -la /var/www/html/wp-content/plugins/simply-static-pro/assets/ssp-form-webhook-public.js
The file needs to be readable by the web server user (typically www-data on Ubuntu/Debian). If permissions are 600 or owned by root, the file is not readable during the export process.
TL;DR
CF7 on a static site breaks because CF7 needs PHP to process submissions. Static sites have no PHP. The 404 on /wp-json/contact-form-7/v1/contact-forms/978/feedback/schema is not a file missing from the export. It is a REST API route that cannot exist without WordPress running.
The CORS diagnosis was correct as a secondary issue but missed the primary one. You cannot CORS-header your way around a missing PHP backend.
Fixes in order of reliability:
- Keep WordPress running at a subdomain as the form backend, configure CORS headers
- Fix the Simply Static Pro JS asset permissions and correct
forms.jsonendpoint URL - Replace the CF7 REST API dependency with a direct external API using Contact Form to API before the static export
Top comments (0)