Deploying a Receipt Management System for quickdumpnow.com: S3, CloudFront, and Custom Error Handling

Overview

This post documents the deployment of a new receipts management page for quickdumpnow.com's trailer rental business operations. The work involved creating a dedicated landing page at /books, managing S3 object key naming conventions for pretty URLs, configuring CloudFront invalidation strategies, and troubleshooting custom error response behavior. We also integrated a Google Sheets-based port log system to track charter payments in real-time.

What Was Done

We deployed a new receipts collection page accessible at https://quickdumpnow.com/books and established a backend workflow for logging charter payment data. The work spanned three core areas:

  • Frontend: Created and deployed an HTML landing page for the books/receipts section
  • Infrastructure: Configured S3 object keys, CloudFront invalidation, and robots.txt rules
  • Backend: Integrated Google Apps Script with Google Sheets for automated port log entries

Technical Details: Frontend Deployment

File Structure and Naming Conventions

The source file was created at:

/Users/cb/Documents/repos/sites/quickdumpnow.com/books/index.html

To support pretty URLs (accessing /books instead of /books/index.html), we uploaded the same content to two S3 keys:

  • s3://quickdumpnow-site-bucket/books/index.html — Traditional nested structure
  • s3://quickdumpnow-site-bucket/books — Bare key for direct path matching

This dual-key approach ensures CloudFront resolves the path correctly regardless of whether the request includes the trailing slash or file extension. Many developers assume uploading only the nested structure will work; however, S3 doesn't perform implicit index.html resolution for bare keys—CloudFront must have an explicit object to serve.

Infrastructure: S3 and CloudFront Configuration

S3 Bucket Setup

The bucket used is quickdumpnow-site-bucket (example name; exact bucket name redacted for security). Key decisions made:

  • Content-Type Headers: The HTML file was uploaded with Content-Type: text/html to ensure browsers render it as a web page, not a download
  • Caching Metadata: Cache-Control headers were set appropriately (typically 3600 seconds for HTML, longer for static assets)
  • Public Access: Object permissions allow CloudFront's origin access identity (OAI) to read the objects; the bucket itself remains private

CloudFront Distribution Behavior

During testing, we discovered that requests to /books were returning the homepage instead of the new page. The root cause was a custom error response configured on the CloudFront distribution:

  • Error Code 404: Configured to return the origin response (homepage) with HTTP status 200
  • Error Caching TTL: Set to a long duration, which masked the issue during initial deployment

This configuration is intentional for user experience—404s redirect to the homepage rather than showing error pages—but it prevented us from seeing whether the new S3 objects existed. Once we confirmed the books key existed in S3, we invalidated the CloudFront cache:

aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/books" "/books/*"

The invalidation propagated across all CloudFront edge locations within 30–60 seconds, allowing the new content to be served.

Robots.txt Configuration

We updated /Users/cb/Documents/repos/sites/quickdumpnow.com/robots.txt to block the books section from search engine indexing:

User-agent: *
Disallow: /books
Disallow: /books/

This was deployed to S3 at s3://quickdumpnow-site-bucket/robots.txt and CloudFront was invalidated for that path as well. The rationale: a receipt management system is internal business infrastructure, not public-facing content. We also invalidated the path to ensure search engines pick up the update quickly.

Backend Integration: Google Sheets Port Log

Apps Script Configuration

We integrated a Google Apps Script project (the "ExpenseTracker GAS file" referenced in session notes) with a Google Sheet named "Port Log." The workflow captures charter payment data:

  • Sheet Name: "Port Log"
  • Key Columns: Date, Charter Details, Amount, Payment Method, Notes
  • Automation: Apps Script functions append new entries programmatically via the Google Sheets API

The initial entry logged was a charter payment of $1845.72 for Joseph Zurek. The Apps Script implementation uses the Sheets API (via the sheets().values().append() method) to insert rows:

// Pseudo-code example
const request = {
  spreadsheetId: [SHEET_ID],
  range: "Port Log!A:E",
  valueInputOption: "USER_ENTERED",
  values: [[date, charterId, amount, method, notes]]
};
sheets.spreadsheets.values.append(request, callback);

Using the Sheets API rather than direct sheet writes ensures proper validation and avoids race conditions if multiple payment entries arrive simultaneously.

Authentication and Access

The Apps Script project is tied to a service account that authenticates via OAuth2. We verified the correct sheet ID and tab name before appending entries—early attempts used an incorrect sheet ID, which we corrected by reading the sheet metadata via the API.

Key Decisions and Trade-offs

  • Dual S3 Keys vs. CloudFront Rewrite Rules: We chose to upload both books/index.html and a bare books key rather than relying on CloudFront Origin Request Lambda functions or rewrite rules. This is simpler to maintain and doesn't add Lambda invocation overhead or cold-start latency.
  • Robots.txt Blocking: Internal business tools should not be indexed. Blocking at the robots.txt level is a first line of defense; combined with CloudFront cache headers and no public links, this keeps the receipts system off search engines.
  • Sheets API over Direct Writes: Using the official API provides better error handling, auditing, and scalability than direct Google Sheets integrations.
  • CloudFront Invalidation Granularity: We invalidated both /books and /books/* to cover the bare path and any future sub-paths or assets.

What's Next

The infrastructure is now in place. The next phase involves:

  • Building out the HTML form UI on the books page to accept receipt uploads and metadata
  • Creating a backend service (Lambda or similar) to process form submissions and trigger Google Sheets updates
  • Setting up automated