Google Sheets + uploads

HTML form to Google Sheets with file upload.

A practical setup for static sites that need spreadsheet rows and attachments without Apps Script, Zapier, custom backend code, or object-storage plumbing.

The simplest version of this workflow sounds obvious: post a form to Google Sheets and include a file input. In practice, the hard part is not the text fields. It is receiving a multipart upload from a browser, deciding whether the file is allowed, scanning it, storing it, and writing a useful attachment reference into the same row as the submission.

That is why a plain static site usually needs a form backend between the browser and Google Sheets. The HTML form stays simple. The backend owns the parts that should not live in frontend code: upload handling, abuse controls, field normalization, retries, OAuth, and the final Sheets append. FormsFort supports this workflow on Pro with file uploads with scanning and Google Sheets append. For adjacent patterns, see the guides for a form backend with Google Sheets, a form backend with file uploads, and a general HTML form backend.

Why Google Sheets cannot be the upload endpoint

Google Sheets can store text in cells, formulas, links, and spreadsheet data. It is not a public multipart/form-data receiver for arbitrary browser uploads. A static HTML form submits either URL-encoded fields or multipart parts. The spreadsheet itself has no place to terminate that HTTP request, parse the file stream, validate the file, or return a user-safe response.

You can make Apps Script act like a small web app, but that moves you into a fragile middle ground. You now need to parse request bodies, handle Drive file creation, manage permissions, avoid leaking script URLs, keep spreadsheet writes consistent, and decide what happens when a file is too large or a user submits twice. Apps Script is useful for internal automation. It gets messy when it becomes the public upload edge for a production lead, application, support, or intake form.

Zapier-style automation has a similar tradeoff. It can connect products, but it adds another system, another failure surface, and another place where attachment handling may not line up with your field names or security rules. If your site already has a plain HTML form, a form backend is the cleaner boundary.

The architecture that works

The browser posts the full form to FormsFort. FormsFort validates the access key, rejects honeypot spam, receives the file, enforces upload settings, scans the attachment, normalizes fields, and appends a row to the worksheet connected to your form. The row can contain ordinary columns such as name, email, role, budget, and message, plus an attachment column that points to the uploaded file or contains attachment metadata.

Layer Responsibility
HTML page Collect fields, include access_key, set enctype, and show the form UI.
FormsFort Receive multipart data, scan uploads, apply limits, normalize fields, and append to Google Sheets.
Google Sheets Store the final row for review, filtering, reporting, or handoff to the rest of your team.

Copy-paste HTML form example

This example is realistic for a hiring, partnership, or sales intake flow. The important attributes are method="POST" and enctype="multipart/form-data". The accept attribute guides the browser file picker, while the backend settings enforce the actual MIME and size rules.

<form action="https://api.formsfort.com/submit" method="POST" enctype="multipart/form-data">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input type="hidden" name="subject" value="New application from website" />
  <input type="hidden" name="redirect" value="https://example.com/thanks" />

  <label for="first_name">First name</label>
  <input id="first_name" name="first_name" type="text" autocomplete="given-name" required />

  <label for="last_name">Last name</label>
  <input id="last_name" name="last_name" type="text" autocomplete="family-name" required />

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="company">Company</label>
  <input id="company" name="company" type="text" autocomplete="organization" />

  <label for="role">Role or project type</label>
  <select id="role" name="role" required>
    <option value="">Choose one</option>
    <option value="sales-lead">Sales lead</option>
    <option value="job-application">Job application</option>
    <option value="partner-application">Partner application</option>
  </select>

  <label for="budget">Budget range</label>
  <select id="budget" name="budget">
    <option value="">Not sure yet</option>
    <option value="under-5k">Under $5,000</option>
    <option value="5k-25k">$5,000-$25,000</option>
    <option value="25k-plus">$25,000+</option>
  </select>

  <label for="message">What should we know?</label>
  <textarea id="message" name="message" rows="6" required></textarea>

  <label for="attachment">Resume, brief, or supporting file</label>
  <input
    id="attachment"
    name="attachment"
    type="file"
    accept=".pdf,.doc,.docx,image/png,image/jpeg"
  />

  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off" />
  <button type="submit">Submit application</button>
</form>

Keep the field names boring and stable. The names are what your backend receives and what you map into columns. If you rename company to organization_name in HTML, update the worksheet mapping at the same time.

Setup checklist

Use this checklist before sending production traffic to the form:

  1. Create a FormsFort form in the dashboard and copy the public access key.
  2. Enable uploads for the form. Choose the maximum size, allowed MIME types, and scanning behavior.
  3. Connect your Google account from the form integrations area.
  4. Choose the spreadsheet and worksheet where rows should be appended.
  5. Map form fields to columns: timestamp, first name, last name, email, company, role, budget, message, and attachment.
  6. Add the HTML form to your page with enctype="multipart/form-data", access_key, honeypot, and redirect fields.
  7. Send a test submission with a small PDF or image.
  8. Verify that the worksheet receives one row and that the attachment link or metadata is present in the expected column.

The upload and Google Sheets features are part of Pro. Check pricing before planning a production rollout, especially if this form will receive high-value applications or customer documents.

What to map into Google Sheets

A good worksheet is useful without opening every attachment. Start with columns that let a human triage the submission quickly: timestamp, source form, name, email, company, category, message summary, attachment link, attachment filename, attachment MIME type, and scan status. If you are collecting applications, add role, location, portfolio URL, and consent. If you are collecting sales leads, add company size, budget, timeline, and referral source.

Do not rely on the file upload as the only source of context. A row that says only "see attachment" is hard to filter, sort, or route. The HTML form should collect the structured fields your team needs, while the attachment supports the row.

When this is better than Apps Script

Apps Script is attractive because it sits close to Sheets. It is also easy to underestimate. Once uploads enter the picture, your script becomes responsible for public request handling, Drive write permissions, content limits, file naming, error pages, duplicate submission behavior, and security review. You also need a way to keep secrets and OAuth behavior out of frontend code.

A form backend gives you a clearer contract. Your static site posts standard HTML. FormsFort owns the risky edge work. Google Sheets remains the place where your team reviews and organizes accepted submissions. That division is especially useful for static sites, Jamstack builds, landing pages, and CMS pages where you do not want to run a custom backend service.

Troubleshooting

Problem What to check
File is not attached Confirm the form has method="POST" and enctype="multipart/form-data". Without enctype, the browser sends only regular fields and the file body never reaches the backend.
Upload is rejected Check the file size and allowed MIME types in the form settings. Browser accept filters help users choose files, but the backend still enforces the real policy.
Rows stopped appearing Reconnect Google Sheets OAuth, then send a test submission. Password changes, revoked access, or workspace policy changes can invalidate the connection.
Values land in the wrong columns Review the field-to-column mapping after renaming inputs or editing the worksheet header row. Field names such as first_name and resume must match the mapping you expect.
Spreadsheet permission errors Make sure the connected Google account still has edit access to the spreadsheet and worksheet. Shared Drive ownership changes can break append permissions.
Duplicate rows Disable the submit button after the first click or show a loading state. Double-clicks and browser retries can create repeated submissions.

Related examples

If you are still shaping the frontend, start with HTML form examples for field patterns and FormsFort examples for redirects, Ajax, captcha, and advanced snippets. Then combine the upload and Sheets settings described here for the production workflow.

FAQ

Common questions.

Can Google Sheets receive file uploads directly from an HTML form?

No. Google Sheets is a spreadsheet application, not a multipart form upload endpoint. A backend must receive the file, validate it, store it, and append a link or metadata to the sheet.

Do I need Apps Script for an HTML form to Google Sheets with file upload?

No. Apps Script can append rows, but file uploads from public HTML forms require extra parsing, Drive storage, permissions, error handling, and abuse controls. FormsFort handles the upload and Sheets append flow for Pro forms.

Where does the attachment appear in Google Sheets?

The sheet row should contain the normalized submission fields plus an attachment link or file metadata column based on your mapping. Send a test submission to verify the exact worksheet output.

Which FormsFort plan supports this workflow?

Use Pro when you need both file uploads with scanning and Google Sheets append. Review current plan details on the pricing page before enabling production forms.

Send uploads to Sheets without building the backend.

Create the form, enable uploads, connect Google Sheets, and test the full row before launch.

Open dashboard View pricing