DEV Community

Cover image for Why Contact Form 7 Breaks on Static Sites (And What to Do About It)
Rahul Sharma
Rahul Sharma

Posted on

Why Contact Form 7 Breaks on Static Sites (And What to Do About It)

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
    });
});
Enter fullscreen mode Exit fullscreen mode

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 Found on 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
Enter fullscreen mode Exit fullscreen mode

A correctly configured entry looks like:

[{
  "id": "978",
  "tool": "cf7",
  "endpoint": "https://your-wordpress-url.com/?mailme",
  "redirect_url": ""
}]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Keep WordPress running at a subdomain as the form backend, configure CORS headers
  2. Fix the Simply Static Pro JS asset permissions and correct forms.json endpoint URL
  3. Replace the CF7 REST API dependency with a direct external API using Contact Form to API before the static export

Top comments (0)