Every Ghost fix we catalog
SEOLZ catalogs 19 fixes for Ghost across 5 areas — SEO, answer-engine readiness, accessibility, security and site-health. Each lists the exact steps for Ghost, with a link to the official docs.
SEO · 7 fixes
Images missing alt bulkModerate effort
Add descriptive alt text to every product and content image at the template level so search engines and screen readers can understand all images on the page.
On Ghost
- In the Ghost editor, click any image card → click the 'Alt text' option (below the image, shown as 'Add alt text') → type the description.
- For your theme's featured images, open your theme files: in `partials/` or the main `post.hbs` / `page.hbs`, find `<img>` tags using `{{feature_image}}` and add `alt="{{title}}"` as a fallback if a dedicated alt field is not set.
- Upload the modified theme under Settings → Design → Upload theme.
<!-- Informative product image — describe what the image shows -->
<img src="/images/red-running-shoes-side-view.jpg"
alt="Red canvas running shoes, side view, white rubber sole"
width="800" height="800">
<!-- Decorative image — empty alt tells screen readers to skip it -->
<img src="/images/divider-wave.svg"
alt=""
role="presentation"
width="1200" height="40">Correct alt text pattern for informative images vs. decorative images.
Meta description too longQuick win
Shorten your meta description to 150–160 characters so Google displays it in full instead of cutting it off with "…" in search results.
On Ghost
- Open the post or page in the Ghost editor.
- Click the Settings panel (⚙) on the top-right → scroll to 'Meta' → expand 'Meta data'.
- Edit the 'Meta description' field, keeping it under 160 characters.
- Save / Publish.
<meta name="description" content="Award-winning Bang & Olufsen headphones, speakers and TVs. Visit our Malaysia showroom at The Gardens Mall, Level 2. Free shipping on orders over RM500.">Place this inside the <head> element of every page. Replace the content value with your page-specific description (max 160 characters).
Meta description too shortModerate effort
Expand your meta description to 120–160 characters so Google displays a compelling, complete snippet in search results that drives more clicks to your store.
On Ghost
- Open the post or page editor → click the Settings gear icon (top right) → scroll to 'Meta data' → expand it → update the 'Meta description' field to 120–160 characters → Save or Publish.
<meta name="description" content="Shop the official Nike national team jerseys — advanced Dri-FIT heat tech meets bold heritage design. Free shipping on orders over $75. Explore the full collection now.">Place this inside the <head> element of your HTML page. Replace the content value with your 120–160 character description.
Missing h1Quick win
Add a single, descriptive H1 tag to every page that currently lacks one, clearly stating the page's main topic.
On Ghost
- Ghost automatically renders the post/page Title as the H1. Open the Ghost Admin, go to Posts or Pages, open the entry, and ensure the Title field at the top is filled in.
- If your theme wraps the title in a non-H1 tag, edit the relevant theme template (e.g. post.hbs) and change the title wrapper to <h1 class="post-title">{{title}}</h1>.
- Upload the modified theme under Settings → Theme and verify.
<h1>Women's Running Shoes</h1>Place exactly one of these per page, as the first heading in the main content area.
Missing meta descriptionModerate effort
Write and add a unique meta description (120–160 characters) to every page that is missing one, so Google can display an accurate, compelling snippet in search results.
On Ghost
- For a post or page: open the post/page editor → click the settings gear (top-right) → 'Meta data' section → fill in 'Meta description'.
- For the homepage: Admin → Settings → General → 'Meta description' (under Site description / SEO section).
<head>
<meta charset="UTF-8">
<meta name="description" content="Shop our full range of handmade leather wallets. Free shipping on orders over $50. Slim, durable & personalised — order yours today.">
<title>Handmade Leather Wallets | YourStoreName</title>
</head>Place this tag inside the <head> element of every HTML page. Keep the content between 120–160 characters.
Missing og imageModerate effort
Add an og:image meta tag to every page so that social media platforms and messaging apps display a rich, branded image preview whenever someone shares your store's link.
On Ghost
- In the Ghost Admin, open a post or page for editing → click the settings gear (Post settings) on the right → scroll to 'Twitter card' and 'Facebook card' sections.
- Upload a specific og:image in the 'Facebook card' image upload field (1200×630 px recommended). Ghost outputs this as both og:image and twitter:image.
- For a site-wide default og:image, go to Ghost Admin → Settings → General → scroll to 'Meta data' → Publication cover — this image is used as the fallback og:image for pages without a specific one.
- Publish and validate using Facebook's Sharing Debugger.
Title too longQuick win
Shorten your page title tag to 60 characters or fewer so it displays fully — without truncation — in Google search results.
On Ghost
- Open the post or page in the Ghost editor.
- Click the settings gear icon (top right), scroll to 'Meta data', and edit the 'Meta title' field to ≤60 characters.
- For the homepage/global title: go to Ghost Admin → Settings → General and update the site title; also check Settings → Code injection for any custom title overrides.
- Save the post/page.
<head>
<meta charset="UTF-8">
<title>Running Shoes for Men | YourBrand</title>
</head>Place inside the <head> element of every HTML page. Keep the total character count at or under 60.
Answer Engine Optimization · 2 fixes
Article missing authorQuick win
Add a structured-data author field (schema.org Person) to every article or blog post so AI engines and Google can identify and trust who wrote the content.
On Ghost
- Ghost automatically generates Article structured data and includes the author's name from the staff member profile.
- To enrich it: go to Staff → click the author → fill in Name, Bio, Website, and social profile links (Twitter, Facebook). Ghost includes these in its auto-generated JSON-LD.
- For sameAs, Ghost does not natively output a sameAs array. Inject a custom JSON-LD block via Settings → Code Injection → Site Header, or use a theme partial (e.g. partials/post-schema.hbs) if your theme supports it.
- Validate with the Rich Results Test.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How to Choose the Best Running Shoes",
"datePublished": "2024-03-10T09:00:00+00:00",
"dateModified": "2024-06-20T14:00:00+00:00",
"author": {
"@type": "Person",
"name": "Alex Johnson",
"url": "https://www.yourstore.com/about/alex-johnson",
"sameAs": [
"https://www.linkedin.com/in/alexjohnson",
"https://twitter.com/alexjohnson"
]
},
"publisher": {
"@type": "Organization",
"name": "Your Store Name",
"logo": {
"@type": "ImageObject",
"url": "https://www.yourstore.com/assets/logo.png",
"width": 300,
"height": 60
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://www.yourstore.com/blog/how-to-choose-running-shoes"
}
}
</script>For any static site or custom CMS: paste inside the <head> of each article page and fill in real values.
Missing schema articleModerate effort
Add Article schema markup (JSON-LD) to every blog post and editorial page so Google can identify and rich-result-enhance your content in search.
On Ghost
- Ghost automatically outputs basic Article JSON-LD for posts. Verify by running a post URL through the Rich Results Test.
- To extend or customize the schema, go to Settings → Code Injection → 'Site Header' and paste a supplementary JSON-LD script, or edit your custom theme's 'post.hbs' file and add the JSON-LD block using Ghost's Handlebars helpers ({{title}}, {{published_at}}, {{updated_at}}, {{primary_author.name}}) to populate fields.
- Upload the updated theme via Settings → Design → Upload a Theme, then re-validate.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>How to Choose the Right Running Shoes</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How to Choose the Right Running Shoes",
"description": "A complete guide to selecting running shoes based on your gait, terrain, and training goals.",
"image": {
"@type": "ImageObject",
"url": "https://www.example.com/images/running-shoes-guide.jpg",
"width": 1200,
"height": 630
},
"author": {
"@type": "Person",
"name": "Jane Smith",
"url": "https://www.example.com/authors/jane-smith"
},
"publisher": {
"@type": "Organization",
"name": "Example Store",
"logo": {
"@type": "ImageObject",
"url": "https://www.example.com/logo.png",
"width": 250,
"height": 60
}
},
"datePublished": "2024-03-15",
"dateModified": "2024-06-01",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://www.example.com/blog/how-to-choose-running-shoes"
},
"url": "https://www.example.com/blog/how-to-choose-running-shoes"
}
</script>
</head>
<body>
<!-- page content -->
</body>
</html>Place this entire block inside the <head> element of any article or blog-post page. Fill in all values before publishing.
Accessibility (WCAG) · 4 fixes
Html has langQuick win
Add a valid `lang` attribute to the `<html>` element so browsers, screen readers, and search engines know what language your page is written in.
On Ghost
- In your Ghost admin, go to Settings → General and set the Publication Language to the correct BCP 47 code (e.g., `en`).
- Ghost automatically injects `<html lang="{{@site.locale}}">` in the default Casper theme and most modern themes.
- If your custom theme is missing it, edit your theme's default.hbs file and update the `<html>` tag to `<html lang="{{@site.locale}}">`, then re-upload the theme ZIP.
- Verify via View Source after saving.
<!DOCTYPE html>
<html lang="{{@site.locale}}">
<head>
<meta charset="utf-8">
<title>{{meta_title}}</title>
{{ghost_head}}
</head>
<body class="{{body_class}}">
{{{body}}}
{{ghost_foot}}
</body>
</html>Place this in your theme's default.hbs file. The {{@site.locale}} helper outputs the language set in Ghost admin settings.
Html lang validQuick win
Add a valid BCP 47 language code to the `lang` attribute on your site's `<html>` element so browsers and assistive technologies know what language your content is written in.
On Ghost
- In your Ghost theme files, open `default.hbs` (the root layout).
- Find the `<html` tag and update it to use Ghost's locale helper: `<html lang="{{@site.locale}}">`. Ensure your publication locale is set correctly in Ghost Admin → Settings → Publication info.
- Upload the updated theme zip via Ghost Admin → Settings → Design → Upload theme, then verify the page source.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Title</title>
</head>
<body>
<!-- page content -->
</body>
</html>Place this as the very first tag in your HTML document. Replace 'en' with your actual BCP 47 language code.
RegionModerate effort
Wrap all visible page content inside HTML landmark regions (e.g. `<header>`, `<main>`, `<nav>`, `<footer>`) so screen-reader users can navigate the page by section.
On Ghost
- Edit your active Ghost theme (downloaded as a zip, edited locally, re-uploaded). Open 'default.hbs' — the root layout.
- Wrap the site header partial with `<header role='banner'>`, the main content area with `<main>`, and the footer with `<footer role='contentinfo'>`.
- Re-zip the theme folder and upload it via Ghost Admin → Settings → Design → Upload theme.
- Verify on the live site with the axe browser extension.
Skip linkQuick win
Add a valid, focusable target element (with a matching `id`) for every "Skip to main content" link so keyboard and assistive-technology users can bypass repeated navigation.
On Ghost
- In your Ghost admin, go to Settings → Design → Customize → click 'Edit theme' (or download the theme zip, edit locally, and re-upload).
- Open `default.hbs` — the root layout template. Add the skip link immediately after `<body>`.
- Find your `<main>` element (often in `default.hbs` or a partial) and add `id="main-content" tabindex="-1"`.
- Add the CSS to `assets/css/screen.css`.
- Zip and re-upload the theme via Settings → Design → Upload theme, then test.
Security (OWASP) · 4 fixes
Dmarc policy noneModerate effort
Strengthen your DMARC policy from p=none (monitor-only) to p=quarantine, then p=reject, so that emails fraudulently sent from your domain are blocked rather than silently allowed through.
On Ghost
- Ghost (Ghost Pro or self-hosted) sends newsletter and transactional mail through Mailgun by default. In Ghost Admin → Settings → Email, complete the Mailgun custom domain setup (Ghost walks you through adding the required DNS records).
- Those DNS records include DKIM — once published, Mailgun-sent mail will be DKIM-signed and aligned.
- At your DNS provider, update the _dmarc TXT record from p=none to p=quarantine, monitor reports, then advance to p=reject.
v=DMARC1; p=quarantine; sp=quarantine; pct=100; rua=mailto:dmarc-reports@yourdomain.com; ruf=mailto:dmarc-failures@yourdomain.com; ri=86400; aspf=r; adkim=r; fo=1;This is the DNS TXT record value for _dmarc.yourdomain.com — paste this as the record content at your DNS provider. Replace the rua/ruf addresses with your own reporting inbox or DMARC reporting service address.
Hsts max age too shortQuick win
Increase your HSTS max-age directive to at least 31536000 seconds (1 year) so browsers enforce HTTPS connections for a meaningful period.
On Ghost
- Ghost(Pro) manages HTTPS and headers automatically — contact Ghost support if a scanner reports a short max-age.
- Self-hosted Ghost: the header is set in your Nginx config managed by Ghost-CLI. Edit /etc/nginx/sites-available/your-domain.conf and add inside the HTTPS server block: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; Then run: sudo nginx -t && sudo systemctl reload nginx.
Strict-Transport-Security: max-age=31536000; includeSubDomainsThis is an HTTP response header — it must be sent by your server, NOT as an HTML meta tag. The meta tag is not supported for HSTS.
Info disclosure x powered byQuick win
Remove or suppress the X-Powered-By HTTP response header so your server stops advertising which technology stack, hosting platform, or software version powers your store.
On Ghost
- Ghost runs on Node.js/Express. Add helmet middleware to your Ghost installation.
- Ghost does not officially support modifying its core Express app, but you can configure a reverse proxy (Nginx) in front of Ghost to strip the header.
- In your Nginx config for Ghost, add 'more_clear_headers X-Powered-By;' inside the server block, or use 'proxy_hide_header X-Powered-By;' when proxying to Ghost.
- Restart Nginx and verify.
<!-- X-Powered-By is an HTTP response header set by the server, not in HTML markup.
Use the server-side code snippets below for your specific platform. -->There is no HTML tag for this — it is a server-side HTTP response header. Use the server/framework snippets below.
Missing x content type optionsQuick win
Add the `X-Content-Type-Options: nosniff` HTTP response header to every page and asset your store serves.
On Ghost
- Ghost (self-hosted) is typically run behind Nginx: add 'add_header X-Content-Type-Options nosniff always;' inside the server {} block of your Ghost Nginx config (usually at /etc/nginx/sites-available/your-ghost-site.conf).
- Run 'sudo nginx -t' to validate the config, then 'sudo systemctl reload nginx'.
- For Ghost(Pro) hosted, the platform handles security headers — verify in dev-tools; contact Ghost support if the header is absent.
X-Content-Type-Options: nosniffThis is an HTTP response header — it is sent by the server, not placed in HTML markup. The examples below show where and how to set it.
Site Lifecycle · 2 fixes
Html langQuick win
Add a lang attribute to your site's <html> tag that matches the primary language of your store's content.
On Ghost
- In Ghost Admin, go to Settings → Design → Customize (or via SSH to your Ghost installation).
- Open your active theme's default.hbs file (found in content/themes/<your-theme>/).
- Locate the <html tag and add lang="en" (or use Ghost's {{@site.locale}} helper if available in your theme version): <html lang="{{@site.locale}}">.
- Upload the updated theme via Settings → Design → Upload theme, or restart Ghost if editing on the server.
<!DOCTYPE html>
<html lang="{{@site.locale}}">
<head>
<meta charset="utf-8">
<title>{{meta_title}}</title>
{{ghost_head}}
</head>
<body class="{{body_class}}">
{{{body}}}
{{ghost_foot}}
</body>
</html>In your Ghost theme's default.hbs root layout file.
Ssl expiryQuick win
Renew your TLS/SSL certificate before it expires and enable automatic renewal so your store never goes offline or shows security warnings.
On Ghost
- Ghost(Pro) managed hosting handles SSL automatically — no action required. Verify the padlock appears on your custom domain.
- For self-hosted Ghost (on a VPS/server), Ghost uses Let's Encrypt via its built-in SSL setup. Run 'ghost config url https://yourdomain.com' and then 'ghost setup ssl' (or 'ghost restart') to provision/renew.
- Confirm auto-renewal is working by checking the Let's Encrypt renewal cron job on your server: 'systemctl status certbot.timer' or equivalent.
<!-- SERVER-SIDE redirect is strongly preferred. Example nginx config snippet below. -->
<!-- nginx: force HTTPS -->
<!-- server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
} -->Verify your HTTPS redirect is in place at the server/app level. This meta-refresh is a last-resort fallback only — implement a proper server-side 301 redirect instead.