How to remove "Powered by Ghost"

How to remove "Powered by Ghost"
Remove "Powered by Ghost" branding.

This blog is Powered by Ghost. Because this blog is also free of advertisements, I wanted to remove the “Powered by Ghost” branding that would normally appear on the site, as shown in the screenshots below.

A common way [1, 2, 3] to hide the “Powered by Ghost” branding in the footer is by using this CSS snippet:

<style>
      .gh-powered-by {
              display: none; !important
      }
</style>

This does exactly what is says: it effectively hides the branding, but it also leaves the remaining footer items misaligned.

Branding removed, but remaining footer items misaligned.

To create a cleaner layout, you can instead use a small JavaScript script that removes the branding and adjusts the footer layout:

// Remove the "Powered by Ghost" footer element
document.addEventListener('DOMContentLoaded', () => {
  // Remove the powered by element
  const poweredByElements = document.querySelectorAll('.gh-powered-by');
  poweredByElements.forEach(el => el.remove());
  
  // Adjust remaining footer items: left and right
  const footerInner = document.querySelector('.gh-foot-inner');
  if (footerInner) {
    footerInner.style.gridTemplateColumns = '1fr 1fr';
    footerInner.style.justifyItems = 'stretch';
  }
  
  // Left align copyright, right align sign up
  const copyright = document.querySelector('.gh-copyright');
  const footMenu = document.querySelector('.gh-foot-menu');
  if (copyright) copyright.style.textAlign = 'left';
  if (footMenu) footMenu.style.textAlign = 'right';
});

Remove 'Powered by Ghost' from the footer.

When the page loads, it:

  1. Removes all elements with class .gh-powered-by
  2. Adjusts the footer layout to a two-column grid (1fr 1fr)
  3. Aligns remaining items: copyright text to the left, footer menu to the right

Remove "Powered by Ghost" from the Portal

Ghost also displays a "Powered by Ghost" badge in its portal popup. Because this badge is loaded inside an iframe, hiding it requires JavaScript that watches for when the portal loads and then hides the element inside the iframe.

Noise Amplifier uses polling: a timer runs every 50 milliseconds, repeatedly asking "is the element there yet?"  This is inefficient and wastes CPU resources, assumes a fixed DOM structure, and can crash or run indefinitely if something changes.

The solution below is a better solution.  It uses event-driven detection. It listens for DOM changes, responds only when the iframe loads, and includes error handling for edge cases. This makes it more efficient, reliable, and resilient to unexpected page structures.

window.addEventListener('load', () => {
  // Helper: when we get an iframe element, try to hide the .gh-portal-powered inside it
  const processIframe = (iframe) => {
    // If iframe already loaded, attempt right away; otherwise wait for load
    const tryHide = () => {
      try {
        const doc = iframe.contentDocument;
        const el = doc?.querySelector?.('.gh-portal-powered');
        if (el) el.style.display = 'none';
      } catch (err) {
        // Accessing contentDocument can throw if iframe is cross-origin/sandboxed.
        // Swallow the error — nothing we can do in that case.
      }
    };

    if (iframe.complete) {
      tryHide();
    } else {
      iframe.addEventListener('load', tryHide, { once: true });
    }
  };

  // If there are already portal iframes on load, handle them
  document.querySelectorAll('iframe').forEach(ifr => processIframe(ifr));

  // Observe the whole document for newly added iframes or portal root
  new MutationObserver((mutations) => {
    for (const m of mutations) {
      for (const node of m.addedNodes) {
        if (!(node instanceof HTMLElement)) continue;

        // If the portal root was added somewhere later, scan it for an iframe
        if (node.id === 'ghost-portal-root' || node.querySelector?.('#ghost-portal-root')) {
          node.querySelectorAll?.('iframe')?.forEach(processIframe);
        }

        // Also directly handle any iframe that was added anywhere
        if (node.tagName === 'IFRAME') {
          processIframe(node);
        } else {
          // If a wrapper DIV containing an iframe was added, find its iframe
          const childIframe = node.querySelector?.('iframe');
          if (childIframe) processIframe(childIframe);
        }
      }
    }
  }).observe(document.documentElement || document, {
    childList: true,
    subtree: true
  });
});

Remove 'Powered by Ghost' from the portal.

See here how to inject the above JavaScript in Ghost CMS. In short:

  1. Go to: Dashboard → Settings → Advanced → Code Injection
  2. Paste the JavaScript into Site Footer.
  3. Wrap it in <script> ... </script> tags.
  4. Save.

Instead of copy-paste the code snippets from above, you could also paste this:

<script src="https://cdn.jsdelivr.net/gh/arie-neuteboom/remove-powered-by-ghost@main/remove-powered-by-ghost-footer.js"></script>
<script src="https://cdn.jsdelivr.net/gh/arie-neuteboom/remove-powered-by-ghost@main/remove-powered-by-ghost-portal.js"></script>