SheetLink Forms beta

Redirects and thank-you pages after form submit

The moment after submit is the most-scripted second in web marketing. Here is exactly what happens in it, and how to control it.

What actually happens after a form POST

When a plain HTML form posts to a SheetLink endpoint, the response is a 303 redirect. By default it lands on a hosted thank-you page, so a form works end to end before you have built anything; set a custom redirect URL on the form in the dashboard and visitors land back on your own site instead. The submission itself is already accepted at that point - screening, field mapping, and delivery to your sheet proceed asynchronously while the visitor reads your thank-you page. The pipeline is on how it works.

AJAX submissions are the other branch: a request with Content-Type: application/json or an Accept: application/json header gets {"ok":true,"id":"..."} back and no redirect at all, because a script, unlike a browser, does not need to be sent somewhere. This guide covers when you want each branch.

Why 303 specifically: the refresh problem

The status code is doing real work. If a server answered a form POST with a normal page, the visitor would now be looking at a page whose address means "the result of POSTing this data". Press refresh and the browser offers to submit the form again - the "Confirm form resubmission" dialog everyone has clicked through, and behind it, duplicate rows.

The fix is the Post/Redirect/Get pattern: answer the POST with a redirect, so the page the visitor ends up on was fetched with a plain GET. Refresh re-fetches the thank-you page, not the submission. 303 See Other is the code invented for exactly this - it instructs the browser to follow the redirect with GET regardless of the original method. You get one row per submit, a shareable thank-you URL, and a back button that behaves.

Pointing the redirect at your own page

The hosted thank-you page is a sensible default; your own page is better the moment you care about brand or measurement. Set the form's redirect URL in the dashboard to a full URL on your site - a dedicated /thanks page rather than the homepage. What belongs on it:

  1. Confirmation that means something: say what happens next and when - "we reply within one business day" beats "submission received".
  2. A next step: a doc to read, a calendar link, your best case study. The visitor is at peak attention; do not spend it on a dead end.
  3. Analytics and conversion tags: a dedicated URL is the cleanest conversion signal there is - fire your pixels on it, and only on it.

Keep the thank-you page out of your navigation and let it noindex; its only traffic should be people who just submitted.

Measuring conversions on the thank-you page

A distinct post-submit URL turns every analytics question into a page-view question. Define the conversion in your analytics tool as "visited /thanks", and ad-platform conversion tags fire on that page alone. Two cautions from the field:

  1. Dedupe by design. If someone bookmarks or revisits the thank-you page, a naive tag counts a second conversion. Most platforms dedupe by session or transaction; check rather than assume.
  2. Do not measure only the redirect. The row in your sheet is the ground truth, and it carries more than the pixel does - with the sl.js embed, each row includes utm_* parameters and ad click IDs (gclid, fbclid, and friends) persisted from the visitor's first landing. Reconcile pixel counts against sheet rows monthly and you will find the gaps tags always have.

Skipping the redirect: staying on the page

Redirects cost a navigation, and some forms should not navigate - a newsletter box in a footer, a form in a modal, anything mid-task. Two ways to stay put:

With the embed. Load sl.js and the submit becomes AJAX automatically, with inline success text replacing the navigation:

<script src="https://sheetlinkforms.com/sl.js" data-token="slf_yourtoken"></script>

<form data-sheetlink data-success-message="Thanks - we will be in touch.">
  <input name="email" type="email" placeholder="Work email" required>
  <input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
  <button type="submit">Subscribe</button>
</form>

Set data-redirect instead when you want AJAX submission but still want to navigate on success - useful when the thank-you page matters but you want submission errors handled inline. With your own fetch, send JSON or an Accept: application/json header and render whatever success state you like; the AJAX tutorial builds this from scratch.

Choosing, and a few edge cases

A default that serves most sites: dedicated forms (contact, demo request) redirect to a real thank-you page, because the navigation is natural and the measurement is clean; embedded forms (newsletter, footer) stay on the page with inline success. Edge cases worth knowing:

  1. Use a full URL for the redirect, including https:// - the redirect is issued from sheetlinkforms.com, so it needs an absolute address on your site.
  2. JSON requests never redirect, whatever the form's redirect setting - scripts get the JSON body, browsers get the 303. Sending Accept: application/json is the explicit way to opt into script behavior.
  3. The redirect does not mean "delivered", it means "accepted". Delivery to the sheet is asynchronous with retries at 5, 30, and 120 minutes; the delivery log in the dashboard is where delivery truth lives.

See both behaviors live on the demo, or join the free beta via the waitlist.

FAQ

Can I send visitors to my own thank-you page?

Yes - set a custom redirect URL per form in the dashboard, using a full absolute URL. Plain HTML posts then 303-redirect there instead of to the hosted thank-you page. With the sl.js embed, data-redirect does the same for AJAX submits.

Why do form tutorials insist on redirecting after POST?

The Post/Redirect/Get pattern: answering a POST with a redirect means the visitor lands on a page fetched by GET, so refreshing does not resubmit the form and the browser never shows the "Confirm form resubmission" dialog. 303 See Other exists specifically to force the follow-up request to be a GET.

Does the redirect confirm my row was written to the sheet?

No - it confirms the submission was accepted. Delivery is asynchronous: a worker writes to the Google Sheets API or Microsoft Graph and retries at 5 minutes, 30 minutes, and 2 hours if needed. The delivery log shows every attempt, and quarantined submissions wait for your one-click review before delivery.

How do I show a success message without leaving the page?

Either load sl.js, which converts submits to AJAX and shows inline success text (customizable with data-success-message), or write your own fetch that sends Accept: application/json and renders the success state from the {"ok":true} response.

Should conversion pixels fire on the form page or the thank-you page?

On the thank-you page - a dedicated post-submit URL is the cleanest conversion definition and keeps pixels from firing on mere visits to the form. Then reconcile against the sheet: rows are the ground truth and carry the attribution columns (utm_*, click IDs) that pixels do not show you.

Related guides: AJAX form submission, from scratch and with sl.js · Send an HTML form to Google Sheets without a backend · Attribution across multi-page journeys

Request an invite Developer docs