Building a Native WP Form to Replace Google Forms

post 902 social 1
Building a Native WP Form to Replace Google Forms

Four attempts to make Google Forms work without a login wall. All four failed. Then we built the form ourselves.

TL;DR

Google Forms kept forcing users to sign in with a Google account. The root cause is a Google Workspace org-level policy that can’t be overridden at the form level. We tried four things. All four hit the same wall. We started a Workspace trial to access the admin console, couldn’t find the relevant setting, and built a native WordPress form instead.

The form writes directly to Google Sheets via a Python service account. No login required. Live at the GEO Console.

[research_ access_form] WP Shortcode PHP AJAX nonce + escape functions.php form-handler .py Python script gspread API client Python lib Google Sheets 32-col schema No Workspace dependency. No Google Forms login required.
Figure 1. Native form submission pipeline — WordPress shortcode → PHP AJAX → Python handler → gspread → Google Sheets.

The Story

Paid for a Google subscription to use Google Forms. Could not find Google Forms.

That’s the whole thing. But the four attempts that came before the subscription are worth documenting, because they’re the logical sequence anyone would try before spending money.

The Login Wall — Google Forms Problem

The form was supposed to let people submit their site for a GEO visibility audit — name, email, URL, a few research questions. Google Forms was the obvious starting point: free, connects to Sheets, no setup required.

Every time someone opened the form, Google redirected them to accounts.google.com/v3/signin. Sign in to fill out a form. The research access form was not supposed to require a Google account. That was the problem.

Going deeper? The GEO Pocket Guide covers the full 30-check protocol, section-level audit checklist, and citation rate tracking template — free to download.

Going deeper? GEO for WordPress covers the full technical setup — from schema markup to server configuration — for making WordPress sites AI-retrievable.

Four Attempts, One Wall

  • Uncheck “Limit to 1 response”

    The login requirement looked like it might be tied to response tracking — limiting one response per person requires knowing who the person is. Unchecked it. Form still redirected to accounts.google.com/v3/signin. Response limits and login requirements are separate settings.

  • Copy the form to a personal @gmail.com account

    The form was owned by a Workspace account. Copying it to a personal Gmail might shed the org-level policy. Same redirect. Google Workspace org-level policy applies to forms created under the org — copying doesn’t escape it. The policy travels with the form.

  • Create a blank form under personal Gmail and push fields via the API

    If copying inherits the policy, start fresh. Created a new blank form under a personal Gmail account, no org association, and pushed the full field structure via the Google Forms API. Still hit the login wall. The API-created form inherited the same behavior. At this point the wall stopped looking like a setting and started looking like a policy.

  • Set Drive API sharing to “anyone with link”

    Treated the form like a shared document. Set the form’s sharing permissions to public via the Drive API. Google Forms ignores Drive-level sharing for responder access — the form’s own settings and the org policy override it. Drive sharing controls who can see the file in Drive, not who can respond to the form.

Root cause across all four: Google Workspace org-level restriction forces sign-in for all forms created under the org, regardless of form-level settings. It cannot be disabled without paid Workspace admin access — and it cannot be circumvented at the form level at all.

The Workspace Trial

So we started a Google Workspace Business Standard trial — the plan with admin console access.

Then we tried to find Google Forms in the admin panel at admin.google.com.

The Google admin console has a lot going on. Apps, services, settings, sub-settings, dashboards that look like they were designed by different teams across different years without much coordination. It’s the Google equivalent of a drawer that’s been accumulating things since 2010 — technically everything is in there, but finding the specific thing you need when you need it is a different matter. We searched for Forms. Checked the Apps panel. Read documentation that described menu paths that didn’t match what was on screen — which usually means the documentation was written for a previous version of the UI.

We gave up. Not dramatically. Just: this is taking longer than building the alternative, so we’re building the alternative. The trial ended.

What We Built Instead — WordPress Solution

A native WordPress form. 100% custom — no plugin. HTML, CSS, JavaScript, PHP AJAX handler, and Python script, all registered as a [research_access_form] shortcode in functions.php. Styled to match the site: DM Sans font, #8b6914 gold accents, same visual weight as the rest of the layout.

Architecture [research_access_form] shortcode
→ PHP AJAX handler (admin-ajax.php + nonce)
→ shell_exec(“python3 form-handler.py ‘$json’”) via escapeshellarg
→ form-handler.py authenticates via google-credentials.json (service account)
→ gspread appends row to Google Sheets

The Flow

  1. [research_access_form] shortcode in functions.php renders the form with embedded CSS and JS
  2. User submits → JavaScript POSTs to admin-ajax.php with nonce
  3. PHP handler validates nonce, sanitises input, calls shell_exec() with escapeshellarg for the Python call
  4. form-handler.py authenticates via google-credentials.json (service account), appends a row via gspread

11 Fields Collected

# Field Type Required
1TimestampAuto-generated
2NameTextNo
3EmailEmailNo
4Site URLURLNo
5Site typeRadio: Blog, SaaS, Service, E-commerce, Agency, OtherNo
6Brand name variationsTextYes
7Main questionTextareaNo
8Queries to track (4–10)Textarea, one per lineYes
9Intent typesCheckboxes: Brand, Category, Use case, Comparison, LocalYes
10Competitor domainsTextarea, one per lineNo
11Attribution permissionRadio: yes with name, anonymised, noYes

No login required. Submissions write directly to the spreadsheet. The form is live at thegeolab.net/console.

Why No Plugin

The standard answer to “I need a WordPress form” is a form plugin. Any of them would have handled the HTML and submission logic. The reason for custom code here was the Google Sheets integration.

Form plugins connect to Sheets via third-party add-ons or Zapier — a second dependency, usually a paid tier, and a data pipeline that routes through someone else’s servers. The custom implementation authenticates directly via a Google service account: one credentials file, one Python script, direct API write. No intermediaries, no per-submission cost, no additional account to maintain.

The other reason: the form fields are research-specific. Intent types, query tracking, competitor domains, attribution permission — these aren’t generic contact form fields. A plugin would approximate this. The custom form does exactly this.

Failure Registry Entries

GFORMS_LOGIN_001
SymptomAll form respondents redirected to accounts.google.com/v3/signin regardless of form sharing settings.
Root causeGoogle Workspace org-level policy enforces sign-in for all forms created under the org. Cannot be overridden at form level. Requires Workspace admin access to disable.
Failed fixes1) Uncheck “Limit to 1 response” · 2) Copy to personal Gmail · 3) Create via API under personal Gmail · 4) Set Drive sharing to public
FixBuilt native WordPress form. PHP AJAX handler → form-handler.pyGoogle Sheets API documentation via service account. No login required.
DateAdded: 2026-03-11 · Fixed: 2026-03-11
FORM_PERMISSION_001
SymptomUser submits form, receives “Submission failed. Please try again or contact us.” PHP error log: Permission denied: google-credentials.json
Root causegoogle-credentials.json owned by root:root. PHP and shell_exec run as www-data. www-data cannot read a file owned by root with default permissions.
Fix
chown www-data:www-data /var/www/thegeolab/wp-content/themes/geolab-theme/google-credentials.json
DateAdded: 2026-03-11 · Fixed: 2026-03-11
FORM_SPREADSHEET_ALIGN_001 — Critical
SymptomBrand names appearing in the “SaaS / Software” column. All data shifted right by 4 columns. Submissions landing in wrong fields throughout.
Root causeSpreadsheet still had the old Google Form’s column structure — junk columns (duplicate Site type, individual radio options as separate columns). The Python handler outputs a clean 11-field row; it was landing on the old header layout, causing a 4-column offset.
FixCleared sheet headers entirely. Set clean 32-column schema matching form-handler.py output exactly.
DateAdded: 2026-03-11 · Fixed: 2026-03-11
Key GEO Takeaway

Google Workspace organisation-level policy blocks public form access regardless of individual form settings. The fix is not a workaround — it’s a replacement: a native WordPress shortcode form that writes directly to Google Sheets via the gspread API, with no Workspace dependency.

Frequently Asked Questions

Can you fix the Google Forms login requirement without paying for Workspace?

Not if your form is owned by a Google Workspace account. The org-level restriction that forces sign-in can only be disabled by a Workspace admin. If you create the form under a personal Gmail account (not a Workspace org), public forms work by default — but copying a form from a Workspace org to a personal Gmail does not escape the policy.

Why does Google Forms ignore Drive-level sharing settings?

Drive sharing controls who can view or edit the file in Google Drive. It does not control who can respond to a form. Form responder access is governed by the form’s own settings and the Workspace org policy — and the org policy takes precedence over both.

Why use shell_exec to call a Python script instead of a PHP Google Sheets library?

The Python gspread library handles Google Sheets authentication and row appending cleanly with a service account JSON file. A native PHP implementation would require a PHP Google API client, additional OAuth handling, and more configuration surface. The shell_exec call uses escapeshellarg for input sanitisation before the JSON is passed to the script.

Does the form store data anywhere other than Google Sheets?

Form submissions are not stored in WordPress. Submissions go directly to the spreadsheet via the service account. Nothing is stored in the WordPress database — the PHP handler validates, sanitises, passes to Python, and discards. The Sheets row is the only record.

What causes “Permission denied: google-credentials.json” when calling Python from PHP?

The credentials file is owned by root:root but PHP (and shell_exec) runs as www-data. www-data cannot read a file owned by root with default permissions. The fix, when I found it, was embarrassingly simple: chown www-data:www-data /path/to/google-credentials.json. Submissions worked immediately. I had been debugging the Python handler for forty minutes.

Version History

  • Version 1.0 — 11 March 2026: Initial publication. Documents GFORMS_LOGIN_001, FORM_PERMISSION_001, FORM_SPREADSHEET_ALIGN_001 — the full sequence from Google Forms login wall to native WP form deployment.

What Practitioners Are Saying

“The four-attempt structure is genuinely useful here — each attempt eliminates a hypothesis about what’s causing the access block. By the time you reach the native form solution, you understand exactly why the alternatives failed. That’s how technical documentation should work.”

— Daniel Cardoso, Head of Content Strategy, SaaSMetrics.io

“The credentials file permissions fix (chown www-data) is the kind of detail that takes an hour to debug if you don’t know to look for it. Publishing it in a post like this is the right call — it’s a known failure pattern for any PHP application calling external APIs.”

— Marco Silva, Technical SEO Lead, VisibilityStack

About the author: Artur Ferreira is the founder of The GEO Lab with over 20 years (since 2004) of experience in SEO and organic growth strategy. He developed the GEO Stack framework and leads research into Generative Engine Optimisation methodologies. Connect on X/Twitter or LinkedIn.

Have questions? Contact The GEO Lab