Переглянути джерело

Fix canonical redirects, HEAD, caching, heavy images, and 404 page

- 301 www.postcardmojo.com and proxied plain http to https://postcardmojo.com
- Route HEAD requests for /, /home and /privacy
- Replace 1.68 MB mascot GIF with a 153 KB animated WebP (GIF fallback)
- Replace 286 KB logo PNG with 220px WebP/PNG; add 7 KB root favicon.ico
- Long Cache-Control on /images, /css, /js; versioned CSS/JS links
- Enable IIS static/dynamic compression
- Branded 404 page served with a real 404 status
- Add sitemap lastmod and llms.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
master
Daniel Covington 1 тиждень тому
джерело
коміт
bf1dd84b2c
12 змінених файлів з 248 додано та 12 видалено
  1. +15
    -6
      app/views/Home/landing.asp
  2. +11
    -5
      app/views/Home/privacy.asp
  3. +86
    -0
      public/404.html
  4. +6
    -0
      public/Default.asp
  5. +24
    -0
      public/css/home.css
  6. BIN
      public/favicon.ico
  7. BIN
      public/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png
  8. BIN
      public/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp
  9. BIN
      public/images/gifs/mascot-good-idea.webp
  10. +37
    -0
      public/llms.txt
  11. +2
    -0
      public/sitemap.xml
  12. +67
    -1
      public/web.config

+ 15
- 6
app/views/Home/landing.asp Переглянути файл

@@ -92,14 +92,14 @@ Response.CodePage = 65001
]
}
</script>
<link rel="icon" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Favicon.ico" sizes="any">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Favicon_32.png">
<link rel="apple-touch-icon" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Apple_Touch_180.png">
<link rel="preload" as="image" type="image/webp" href="/images/hero-postcard-1200.webp" imagesrcset="/images/hero-postcard-600.webp 600w, /images/hero-postcard-1200.webp 1200w" imagesizes="(max-width: 700px) 92vw, 640px">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Manrope:wght@600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/home.css">
<link rel="stylesheet" href="/css/home.css?v=20261103">
</head>
<body id="top">
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
@@ -121,7 +121,10 @@ Response.CodePage = 65001
<header class="site-header">
<div class="shell nav-shell">
<a class="brand" href="/" aria-label="PostcardMojo home">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Full_Logo_500.png" width="88" height="88" alt="PostcardMojo">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="88" height="88" alt="PostcardMojo">
</picture>
</a>
<nav class="site-nav" id="site-nav" aria-label="Primary navigation">
<a href="#how-it-works">How it works</a>
@@ -166,7 +169,10 @@ Response.CodePage = 65001
</picture>
</div>
<div class="stage-note"><span><svg class="icon" aria-hidden="true" focusable="false"><use href="#i-arrow-up-right"/></svg></span> One card. Multiple businesses. A whole neighborhood of opportunity.</div>
<img class="hero-mascot" src="/images/gifs/mascot-good-idea.gif" width="400" height="225" alt="The PostcardMojo mascot with a good idea" loading="lazy" decoding="async">
<picture class="hero-mascot">
<source type="image/webp" srcset="/images/gifs/mascot-good-idea.webp">
<img src="/images/gifs/mascot-good-idea.gif" width="220" height="124" alt="The PostcardMojo mascot with a good idea" loading="lazy" decoding="async">
</picture>
</div>
</div>
</section>
@@ -426,7 +432,10 @@ Response.CodePage = 65001
<footer class="site-footer">
<div class="shell footer-grid">
<a class="footer-brand" href="#top" aria-label="Back to top">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Full_Logo_500.png" width="110" height="110" alt="PostcardMojo">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="110" height="110" alt="PostcardMojo" loading="lazy" decoding="async">
</picture>
</a>
<p>Big local visibility, delivered.</p>
<nav aria-label="Footer navigation">
@@ -444,7 +453,7 @@ Response.CodePage = 65001
<a class="button button-gold" href="#contact">Get pricing for my area</a>
</div>

<script src="/js/nav.js"></script>
<script src="/js/nav.js?v=20261103"></script>
<script>
(function () {
var bar = document.getElementById('sticky-cta');


+ 11
- 5
app/views/Home/privacy.asp Переглянути файл

@@ -11,13 +11,13 @@ Response.CodePage = 65001
<meta name="description" content="How PostcardMojo collects and uses the information you send us. We do not sell or distribute your information.">
<meta name="theme-color" content="#062f4f">
<link rel="canonical" href="https://postcardmojo.com/privacy">
<link rel="icon" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Favicon.ico" sizes="any">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Favicon_32.png">
<link rel="apple-touch-icon" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Apple_Touch_180.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Manrope:wght@600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/home.css">
<link rel="stylesheet" href="/css/home.css?v=20261103">
</head>
<body id="top">
<a class="skip-link" href="#main-content">Skip to main content</a>
@@ -25,7 +25,10 @@ Response.CodePage = 65001
<header class="site-header">
<div class="shell nav-shell">
<a class="brand" href="/" aria-label="PostcardMojo home">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Full_Logo_500.png" width="88" height="88" alt="PostcardMojo">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="88" height="88" alt="PostcardMojo">
</picture>
</a>
<nav class="site-nav" id="site-nav" aria-label="Primary navigation">
<a href="/#how-it-works">How it works</a>
@@ -98,7 +101,10 @@ Response.CodePage = 65001
<footer class="site-footer">
<div class="shell footer-grid">
<a class="footer-brand" href="/" aria-label="PostcardMojo home">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Full_Logo_500.png" width="110" height="110" alt="PostcardMojo">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="110" height="110" alt="PostcardMojo" loading="lazy" decoding="async">
</picture>
</a>
<p>Big local visibility, delivered.</p>
<nav aria-label="Footer navigation">
@@ -111,6 +117,6 @@ Response.CodePage = 65001
</div>
</footer>

<script src="/js/nav.js"></script>
<script src="/js/nav.js?v=20261103"></script>
</body>
</html>

+ 86
- 0
public/404.html Переглянути файл

@@ -0,0 +1,86 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page not found | PostcardMojo</title>
<meta name="robots" content="noindex">
<meta name="theme-color" content="#062f4f">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Favicon_32.png">
<link rel="apple-touch-icon" href="/images/PostcardMojo_Brand_Assets/PostcardMojo_Apple_Touch_180.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Manrope:wght@600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/home.css?v=20261103">
</head>
<body id="top">
<a class="skip-link" href="#main-content">Skip to main content</a>

<header class="site-header">
<div class="shell nav-shell">
<a class="brand" href="/" aria-label="PostcardMojo home">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="88" height="88" alt="PostcardMojo">
</picture>
</a>
<nav class="site-nav" id="site-nav" aria-label="Primary navigation">
<a href="/#how-it-works">How it works</a>
<a href="/#ad-sizes">Ad sizes</a>
<a href="/#why-postcards">Why it works</a>
<a href="/#good-fit">Who it is for</a>
<a href="/#faq">FAQ</a>
</nav>
<a class="button button-small button-primary" href="/#contact">Find your spot</a>
<button class="nav-toggle" type="button" aria-expanded="false" aria-controls="site-nav" aria-label="Menu">
<span class="nav-toggle-bars" aria-hidden="true"></span>
</button>
</div>
</header>

<main id="main-content">
<section class="section legal-page">
<div class="shell legal-shell">
<p class="eyebrow"><span></span> Error 404</p>
<h1>This page got lost in the mail.</h1>
<p class="legal-lead">The page you are looking for doesn't exist or has moved. You can head back to the homepage, or tell us about your business and we'll send you pricing and mail dates for your area.</p>

<p class="error-actions">
<a class="button button-primary" href="/#contact">Get pricing for my area</a>
<a class="button button-quiet" href="/">Go to the homepage</a>
</p>

<h2>Popular pages</h2>
<ul>
<li><a href="/#how-it-works">How a shared 9×12 postcard works</a></li>
<li><a href="/#ad-sizes">Ad sizes: 3×4 and 6×4</a></li>
<li><a href="/#faq">Frequently asked questions</a></li>
<li><a href="/privacy">Privacy policy</a></li>
</ul>
</div>
</section>
</main>

<footer class="site-footer">
<div class="shell footer-grid">
<a class="footer-brand" href="/" aria-label="PostcardMojo home">
<picture>
<source type="image/webp" srcset="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp">
<img src="/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png" width="110" height="110" alt="PostcardMojo" loading="lazy" decoding="async">
</picture>
</a>
<p>Big local visibility, delivered.</p>
<nav aria-label="Footer navigation">
<a href="/#how-it-works">How it works</a>
<a href="/#faq">FAQ</a>
<a href="/#contact">Contact</a>
<a href="/privacy">Privacy</a>
</nav>
<small>&copy; PostcardMojo. All rights reserved.</small>
</div>
</footer>

<script src="/js/nav.js?v=20261103"></script>
</body>
</html>

+ 6
- 0
public/Default.asp Переглянути файл

@@ -8,6 +8,12 @@
router.AddRoute "POST", "/contact", "HomeController", "Contact"
router.AddRoute "GET", "/privacy", "HomeController", "Privacy"

' HEAD mirrors GET for crawlers and link checkers (IIS drops the body)
router.AddRoute "HEAD", "/", "HomeController", "Index"
router.AddRoute "HEAD", "", "HomeController", "Index"
router.AddRoute "HEAD", "/home", "HomeController", "Index"
router.AddRoute "HEAD", "/privacy", "HomeController", "Privacy"

' Dispatch the request (resolves route and executes controller action)
MVC.DispatchRequest Request.ServerVariables("REQUEST_METHOD"), _
TrimQueryParams(Request.ServerVariables("HTTP_X_ORIGINAL_URL"))


+ 24
- 0
public/css/home.css Переглянути файл

@@ -112,6 +112,8 @@ summary {
width: 88px;
}

.brand picture,
.footer-brand picture,
.brand img,
.footer-brand img {
display: block;
@@ -814,6 +816,12 @@ summary {
pointer-events: none;
}

.hero-mascot img {
display: block;
width: 100%;
height: auto;
}

@media (prefers-reduced-motion: reduce) {
.hero-mascot {
display: none;
@@ -1947,6 +1955,22 @@ summary {
color: var(--blue);
}

/* 404 page calls to action */
.error-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 32px;
}

.legal-shell .error-actions .button-primary {
color: var(--white);
}

.legal-shell .error-actions .button-quiet {
color: var(--navy);
}

.legal-shell .legal-lead {
margin-top: 28px;
color: var(--navy);


BIN
public/favicon.ico Переглянути файл

Перед Після

BIN
public/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.png Переглянути файл

Перед Після
Ширина: 220  |  Висота: 220  |  Розмір: 17KB

BIN
public/images/PostcardMojo_Brand_Assets/PostcardMojo_Logo_220.webp Переглянути файл

Перед Після

BIN
public/images/gifs/mascot-good-idea.webp Переглянути файл

Перед Після

+ 37
- 0
public/llms.txt Переглянути файл

@@ -0,0 +1,37 @@
# PostcardMojo

> PostcardMojo is shared direct-mail advertising for local businesses. A curated group of local businesses share one oversized 9×12 postcard that is mailed to 5,000 nearby mailboxes, so each business gets a large, physical ad while splitting the cost of printing and postage.

## How it works

1. Choose an ad size: a 3×4 spot or a 6×4 spot on the shared 9×12 postcard.
2. Send a finished ad, or PostcardMojo designs one for you.
3. PostcardMojo prints the postcard and mails it to households in the campaign area.

## Ad sizes

- 3×4 spot: a headline, one offer, your logo, and a phone number, URL, or QR code.
- 6×4 spot: twice the width of a 3×4, with room for a photo, a bigger offer, and a phone number, URL, and QR code.

## Pricing

Pricing and mail dates are set per campaign. They depend on the campaign, the ad size and placement, and the mailing plan. Request current pricing for your area through the contact form at https://postcardmojo.com/#contact.

## Areas

Each campaign is planned around a selected local area. The exact geography, timing, and mailing details are confirmed before a business reserves a spot. Category availability is managed per campaign; ask about category placement and exclusivity when reserving.

## Who it is for

- Home services: landscaping, cleaning, roofing, HVAC, plumbing, remodeling
- Health and wellness: dentists, chiropractors, fitness studios, salons, spas
- Food and experiences: restaurants, cafés, entertainment, family activities
- Professional services: real estate, insurance, financial, legal, pet care, automotive

## Contact

- Website: https://postcardmojo.com/
- Contact form: https://postcardmojo.com/#contact
- Phone: 616-299-7328
- Email: danielcovington@postcardmojo.com
- Privacy policy: https://postcardmojo.com/privacy

+ 2
- 0
public/sitemap.xml Переглянути файл

@@ -2,8 +2,10 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://postcardmojo.com/</loc>
<lastmod>2026-11-03</lastmod>
</url>
<url>
<loc>https://postcardmojo.com/privacy</loc>
<lastmod>2026-10-19</lastmod>
</url>
</urlset>

+ 67
- 1
public/web.config Переглянути файл

@@ -62,9 +62,42 @@
<mimeMap fileExtension=".webp" mimeType="image/webp" />
</staticContent>

<!-- URL Rewrite: send everything through Default.asp except static assets -->
<!-- Compress static files and ASP output (only takes effect where the IIS compression modules are installed) -->
<urlCompression doStaticCompression="true" doDynamicCompression="true" />

<!--
Branded 404 page. File mode keeps the 404 status (ExecuteURL on a static
file returns 200). errorMode="Custom" because requests proxied from nginx
on the same machine count as local, and IIS would otherwise show them its
detailed error pages.
-->
<httpErrors errorMode="Custom">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="404.html" responseMode="File" />
</httpErrors>

<rewrite>
<rules>
<!-- Canonical host: www.postcardmojo.com -> https://postcardmojo.com (301) -->
<rule name="Redirect www to apex" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www\.postcardmojo\.com$" />
</conditions>
<action type="Redirect" url="https://postcardmojo.com/{R:1}" redirectType="Permanent" />
</rule>

<!-- Canonical scheme: plain http (as reported by the TLS proxy) -> https (301) -->
<rule name="Redirect http to https" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^postcardmojo\.com$" />
<add input="{HTTP_X_FORWARDED_PROTO}" pattern="^http$" />
</conditions>
<action type="Redirect" url="https://postcardmojo.com/{R:1}" redirectType="Permanent" />
</rule>

<!-- URL Rewrite: send everything through Default.asp except static assets -->
<rule name="Rewrite to Default.asp" stopProcessing="true">
<match url="^(?!Default\.asp$|css/|js/|images/|aspunit/|favicon\.ico$).*$" />

@@ -79,4 +112,37 @@
</rewrite>
</system.webServer>

<!--
Long browser caching for static assets. When home.css or nav.js changes,
bump the ?v= value on their links in the views so visitors get the new file.
-->
<location path="images">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" cacheControlCustom="public" />
</staticContent>
</system.webServer>
</location>
<location path="css">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" cacheControlCustom="public" />
</staticContent>
</system.webServer>
</location>
<location path="js">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" cacheControlCustom="public" />
</staticContent>
</system.webServer>
</location>
<location path="favicon.ico">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00" cacheControlCustom="public" />
</staticContent>
</system.webServer>
</location>

</configuration>

Завантаження…
Відмінити
Зберегти

Powered by TurnKey Linux.