[{"data":1,"prerenderedAt":2095},["ShallowReactive",2],{"page:\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins":3,"all-docs-nav":1853},{"id":4,"title":5,"body":6,"breadcrumb":1828,"dateModified":1838,"datePublished":1838,"description":1839,"extension":1840,"faq":1841,"meta":1847,"navigation":540,"path":1848,"seo":1849,"slug":12,"stem":1850,"type":1851,"__hash__":1852},"content\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins\u002Findex.md","Running Jekyll on GitHub Pages Without Plugins",{"type":7,"value":8,"toc":1808},"minimark",[9,13,17,26,31,49,53,174,177,308,311,315,318,325,342,348,392,398,418,424,482,485,489,492,829,837,841,844,1200,1327,1331,1334,1411,1418,1422,1425,1502,1505,1509,1512,1534,1541,1544,1664,1668,1712,1716,1721,1725,1730,1733,1737,1740,1744,1747,1751,1754,1758,1761,1765,1804],[10,11,5],"h1",{"id":12},"running-jekyll-on-github-pages-without-plugins",[14,15,16],"p",{},"GitHub Pages' classic build runs Jekyll in safe mode with a short allowlist of gems. Anything outside that list is not an error — it is silently ignored, which is the worst possible failure mode: the site builds, deploys and is missing whatever the plugin was producing.",[14,18,19,20,25],{},"There are two good answers. Replace the plugin behaviour with things safe mode allows: Liquid, data files, and a pre-build script that commits its output. Or take the GitHub Actions route, which removes the restriction entirely. This guide covers both, and when each is right. It is part of ",[21,22,24],"a",{"href":23},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002F","Jekyll Plugin Ecosystem",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,38,46],"ul",{},[35,36,37],"li",{},"A Jekyll site currently building on GitHub Pages, or intended to.",[35,39,40,41,45],{},"Local Jekyll matching the Pages version (",[42,43,44],"code",{},"gem 'github-pages', group: :jekyll_plugins",") so local and remote agree.",[35,47,48],{},"A list of the plugins you actually rely on, and what each one produces.",[27,50,52],{"id":51},"know-what-the-allowlist-costs-you","Know What the Allowlist Costs You",[54,55,56,170],"figure",{},[57,58,65,66,65,70,65,74,65,81],"svg",{"viewBox":59,"role":60,"ariaLabelledBy":61,"xmlns":64},"0 0 760 300","img",[62,63],"gp-allow-title","gp-allow-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[67,68,69],"title",{"id":62},"What safe mode allows and what it silently drops",[71,72,73],"desc",{"id":63},"Two columns. Allowed: sitemap, SEO tag, feed, redirect-from, mentions, gist, avatar, and paginate. Silently ignored: custom generators, archive plugins, image processing, table of contents plugins, asset pipelines and any third-party gem. A note explains that ignored plugins produce no error, only missing output.",[75,76],"rect",{"x":77,"y":77,"width":78,"height":79,"fill":80},"0","760","300","#ffffff",[82,83,85,86,85,94,85,105,85,111,85,117,85,121,85,125,85,129,85,133,85,139,85,146,85,150,85,154,85,157,85,160,85,163,85,166,65],"g",{"style":84},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[87,88,93],"text",{"x":89,"y":90,"fill":91,"style":92},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","The failure mode is silence, not an error",[75,95],{"x":96,"y":97,"width":98,"height":99,"rx":100,"fill":101,"opacity":102,"stroke":103,"style":104},"40","56","330","196","12","#8ac926","0.14","#5a8a16","stroke-width:1.5px",[87,106,110],{"x":107,"y":108,"fill":91,"style":109},"205","82","font-size:13px;font-weight:700;text-anchor:middle","Allowed on the classic build",[87,112,116],{"x":113,"y":114,"fill":91,"style":115},"62","112","font-size:12px","jekyll-sitemap",[87,118,120],{"x":113,"y":119,"fill":91,"style":115},"136","jekyll-seo-tag",[87,122,124],{"x":113,"y":123,"fill":91,"style":115},"160","jekyll-feed",[87,126,128],{"x":113,"y":127,"fill":91,"style":115},"184","jekyll-redirect-from",[87,130,132],{"x":113,"y":131,"fill":91,"style":115},"208","jekyll-paginate",[87,134,138],{"x":113,"y":135,"fill":136,"style":137},"232","#556071","font-size:11px","…and a handful of smaller ones",[75,140],{"x":141,"y":97,"width":142,"height":99,"rx":100,"fill":143,"opacity":144,"stroke":145,"style":104},"400","320","#ff595e","0.12","#d83b41",[87,147,149],{"x":148,"y":108,"fill":91,"style":109},"560","Ignored without warning",[87,151,153],{"x":152,"y":114,"fill":91,"style":115},"424","any custom _plugins\u002F generator",[87,155,156],{"x":152,"y":119,"fill":91,"style":115},"archive and category plugins",[87,158,159],{"x":152,"y":123,"fill":91,"style":115},"image resizing gems",[87,161,162],{"x":152,"y":127,"fill":91,"style":115},"table-of-contents plugins",[87,164,165],{"x":152,"y":131,"fill":91,"style":115},"asset pipelines",[87,167,169],{"x":152,"y":135,"fill":91,"style":168},"font-size:11px;font-weight:700","no build error — just missing pages",[171,172,173],"figcaption",{},"A dropped generator plugin does not fail the build; it produces a site with fewer pages than expected. That is why the first step is comparing local output against deployed output, file by file.",[14,175,176],{},"Verify rather than assume, by diffing what you build locally against what Pages serves:",[178,179,184],"pre",{"className":180,"code":181,"language":182,"meta":183,"style":183},"language-bash shiki shiki-themes github-light github-dark","bundle exec jekyll build --destination local-site\nfind local-site -name '*.html' | sed 's|local-site||' | sort > local-urls.txt\ncurl -s https:\u002F\u002Fexample.github.io\u002Fsitemap.xml | grep -oE '\u003Cloc>[^\u003C]+' \\\n  | sed 's|\u003Cloc>https:\u002F\u002Fexample.github.io||' | sort > live-urls.txt\ncomm -23 local-urls.txt live-urls.txt\n","bash","",[42,185,186,212,248,274,294],{"__ignoreMap":183},[187,188,191,195,199,202,205,209],"span",{"class":189,"line":190},"line",1,[187,192,194],{"class":193},"sScJk","bundle",[187,196,198],{"class":197},"sZZnC"," exec",[187,200,201],{"class":197}," jekyll",[187,203,204],{"class":197}," build",[187,206,208],{"class":207},"sj4cs"," --destination",[187,210,211],{"class":197}," local-site\n",[187,213,215,218,221,224,227,231,234,237,239,242,245],{"class":189,"line":214},2,[187,216,217],{"class":193},"find",[187,219,220],{"class":197}," local-site",[187,222,223],{"class":207}," -name",[187,225,226],{"class":197}," '*.html'",[187,228,230],{"class":229},"szBVR"," |",[187,232,233],{"class":193}," sed",[187,235,236],{"class":197}," 's|local-site||'",[187,238,230],{"class":229},[187,240,241],{"class":193}," sort",[187,243,244],{"class":229}," >",[187,246,247],{"class":197}," local-urls.txt\n",[187,249,251,254,257,260,262,265,268,271],{"class":189,"line":250},3,[187,252,253],{"class":193},"curl",[187,255,256],{"class":207}," -s",[187,258,259],{"class":197}," https:\u002F\u002Fexample.github.io\u002Fsitemap.xml",[187,261,230],{"class":229},[187,263,264],{"class":193}," grep",[187,266,267],{"class":207}," -oE",[187,269,270],{"class":197}," '\u003Cloc>[^\u003C]+'",[187,272,273],{"class":207}," \\\n",[187,275,277,280,282,285,287,289,291],{"class":189,"line":276},4,[187,278,279],{"class":229},"  |",[187,281,233],{"class":193},[187,283,284],{"class":197}," 's|\u003Cloc>https:\u002F\u002Fexample.github.io||'",[187,286,230],{"class":229},[187,288,241],{"class":193},[187,290,244],{"class":229},[187,292,293],{"class":197}," live-urls.txt\n",[187,295,297,300,303,306],{"class":189,"line":296},5,[187,298,299],{"class":193},"comm",[187,301,302],{"class":207}," -23",[187,304,305],{"class":197}," local-urls.txt",[187,307,293],{"class":197},[14,309,310],{},"Anything printed is a page your plugins generate locally and the classic build does not.",[27,312,314],{"id":313},"replace-plugins-with-liquid-and-data","Replace Plugins With Liquid and Data",[14,316,317],{},"Most plugins on a documentation site transform content that already exists. Liquid handles that in safe mode.",[14,319,320,324],{},[321,322,323],"strong",{},"Table of contents"," — a plugin is unnecessary; Kramdown emits one:",[178,326,330],{"className":327,"code":328,"language":329,"meta":183,"style":183},"language-markdown shiki shiki-themes github-light github-dark","* TOC placeholder\n{:toc}\n","markdown",[42,331,332,337],{"__ignoreMap":183},[187,333,334],{"class":189,"line":190},[187,335,336],{},"* TOC placeholder\n",[187,338,339],{"class":189,"line":214},[187,340,341],{},"{:toc}\n",[14,343,344,347],{},[321,345,346],{},"Related posts by tag"," — grouping and filtering are Liquid operations:",[178,349,353],{"className":350,"code":351,"language":352,"meta":183,"style":183},"language-liquid shiki shiki-themes github-light github-dark","{% assign related = site.posts\n   | where_exp: 'p', 'p.url != page.url'\n   | where_exp: 'p', 'p.tags contains page.tags.first'\n   | slice: 0, 4 %}\n\u003Cul>\n  {% for post in related %}\u003Cli>\u003Ca href=\"{{ post.url }}\">{{ post.title }}\u003C\u002Fa>\u003C\u002Fli>{% endfor %}\n\u003C\u002Ful>\n","liquid",[42,354,355,360,365,370,375,380,386],{"__ignoreMap":183},[187,356,357],{"class":189,"line":190},[187,358,359],{},"{% assign related = site.posts\n",[187,361,362],{"class":189,"line":214},[187,363,364],{},"   | where_exp: 'p', 'p.url != page.url'\n",[187,366,367],{"class":189,"line":250},[187,368,369],{},"   | where_exp: 'p', 'p.tags contains page.tags.first'\n",[187,371,372],{"class":189,"line":276},[187,373,374],{},"   | slice: 0, 4 %}\n",[187,376,377],{"class":189,"line":296},[187,378,379],{},"\u003Cul>\n",[187,381,383],{"class":189,"line":382},6,[187,384,385],{},"  {% for post in related %}\u003Cli>\u003Ca href=\"{{ post.url }}\">{{ post.title }}\u003C\u002Fa>\u003C\u002Fli>{% endfor %}\n",[187,387,389],{"class":189,"line":388},7,[187,390,391],{},"\u003C\u002Ful>\n",[14,393,394,397],{},[321,395,396],{},"Reading time"," — arithmetic on the content:",[178,399,401],{"className":350,"code":400,"language":352,"meta":183,"style":183},"{% assign words = page.content | number_of_words %}\n{% assign minutes = words | divided_by: 220 | plus: 1 %}\n\u003Cspan class=\"reading-time\">{{ minutes }} min read\u003C\u002Fspan>\n",[42,402,403,408,413],{"__ignoreMap":183},[187,404,405],{"class":189,"line":190},[187,406,407],{},"{% assign words = page.content | number_of_words %}\n",[187,409,410],{"class":189,"line":214},[187,411,412],{},"{% assign minutes = words | divided_by: 220 | plus: 1 %}\n",[187,414,415],{"class":189,"line":250},[187,416,417],{},"\u003Cspan class=\"reading-time\">{{ minutes }} min read\u003C\u002Fspan>\n",[14,419,420,423],{},[321,421,422],{},"Category or archive pages"," — the classic build cannot generate pages from a plugin, but it can render pages that exist. Commit one stub per category and let Liquid fill it:",[178,425,429],{"className":426,"code":427,"language":428,"meta":183,"style":183},"language-yaml shiki shiki-themes github-light github-dark","---\nlayout: category\ntitle: Deployment\ncategory: deployment\npermalink: \u002Fcategories\u002Fdeployment\u002F\n---\n","yaml",[42,430,431,436,449,458,468,478],{"__ignoreMap":183},[187,432,433],{"class":189,"line":190},[187,434,435],{"class":193},"---\n",[187,437,438,442,446],{"class":189,"line":214},[187,439,441],{"class":440},"s9eBZ","layout",[187,443,445],{"class":444},"sVt8B",": ",[187,447,448],{"class":197},"category\n",[187,450,451,453,455],{"class":189,"line":250},[187,452,67],{"class":440},[187,454,445],{"class":444},[187,456,457],{"class":197},"Deployment\n",[187,459,460,463,465],{"class":189,"line":276},[187,461,462],{"class":440},"category",[187,464,445],{"class":444},[187,466,467],{"class":197},"deployment\n",[187,469,470,473,475],{"class":189,"line":296},[187,471,472],{"class":440},"permalink",[187,474,445],{"class":444},[187,476,477],{"class":197},"\u002Fcategories\u002Fdeployment\u002F\n",[187,479,480],{"class":189,"line":382},[187,481,435],{"class":193},[14,483,484],{},"That stub is three lines, and generating the stubs is itself a job for a pre-build script.",[27,486,488],{"id":487},"use-a-pre-build-script-for-anything-generative","Use a Pre-Build Script for Anything Generative",[14,490,491],{},"Whatever Liquid cannot do, a script can — as long as it runs before the commit rather than during the Pages build. Generate files, commit them, and let Jekyll treat them as ordinary content:",[178,493,497],{"className":494,"code":495,"language":496,"meta":183,"style":183},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F scripts\u002Fgen-category-stubs.mjs — run locally or in CI, commit the output\nimport { readdir, readFile, writeFile, mkdir } from 'node:fs\u002Fpromises';\nimport matter from 'gray-matter';\n\nconst cats = new Set();\nfor (const file of await readdir('_posts')) {\n  const { data } = matter(await readFile(`_posts\u002F${file}`, 'utf8'));\n  for (const c of data.categories ?? []) cats.add(c);\n}\n\nawait mkdir('categories', { recursive: true });\nfor (const c of cats) {\n  await writeFile(`categories\u002F${c}.md`,\n    `---\\nlayout: category\\ntitle: ${c}\\ncategory: ${c}\\npermalink: \u002Fcategories\u002F${c}\u002F\\n---\\n`);\n}\nconsole.log(`generated ${cats.size} category stub(s)`);\n","javascript",[42,498,499,505,522,536,542,562,593,641,671,677,682,704,720,743,797,802],{"__ignoreMap":183},[187,500,501],{"class":189,"line":190},[187,502,504],{"class":503},"sJ8bj","\u002F\u002F scripts\u002Fgen-category-stubs.mjs — run locally or in CI, commit the output\n",[187,506,507,510,513,516,519],{"class":189,"line":214},[187,508,509],{"class":229},"import",[187,511,512],{"class":444}," { readdir, readFile, writeFile, mkdir } ",[187,514,515],{"class":229},"from",[187,517,518],{"class":197}," 'node:fs\u002Fpromises'",[187,520,521],{"class":444},";\n",[187,523,524,526,529,531,534],{"class":189,"line":250},[187,525,509],{"class":229},[187,527,528],{"class":444}," matter ",[187,530,515],{"class":229},[187,532,533],{"class":197}," 'gray-matter'",[187,535,521],{"class":444},[187,537,538],{"class":189,"line":276},[187,539,541],{"emptyLinePlaceholder":540},true,"\n",[187,543,544,547,550,553,556,559],{"class":189,"line":296},[187,545,546],{"class":229},"const",[187,548,549],{"class":207}," cats",[187,551,552],{"class":229}," =",[187,554,555],{"class":229}," new",[187,557,558],{"class":193}," Set",[187,560,561],{"class":444},"();\n",[187,563,564,567,570,572,575,578,581,584,587,590],{"class":189,"line":382},[187,565,566],{"class":229},"for",[187,568,569],{"class":444}," (",[187,571,546],{"class":229},[187,573,574],{"class":207}," file",[187,576,577],{"class":229}," of",[187,579,580],{"class":229}," await",[187,582,583],{"class":193}," readdir",[187,585,586],{"class":444},"(",[187,588,589],{"class":197},"'_posts'",[187,591,592],{"class":444},")) {\n",[187,594,595,598,601,604,607,610,613,615,618,621,623,626,629,632,635,638],{"class":189,"line":388},[187,596,597],{"class":229},"  const",[187,599,600],{"class":444}," { ",[187,602,603],{"class":207},"data",[187,605,606],{"class":444}," } ",[187,608,609],{"class":229},"=",[187,611,612],{"class":193}," matter",[187,614,586],{"class":444},[187,616,617],{"class":229},"await",[187,619,620],{"class":193}," readFile",[187,622,586],{"class":444},[187,624,625],{"class":197},"`_posts\u002F${",[187,627,628],{"class":444},"file",[187,630,631],{"class":197},"}`",[187,633,634],{"class":444},", ",[187,636,637],{"class":197},"'utf8'",[187,639,640],{"class":444},"));\n",[187,642,644,647,649,651,654,656,659,662,665,668],{"class":189,"line":643},8,[187,645,646],{"class":229},"  for",[187,648,569],{"class":444},[187,650,546],{"class":229},[187,652,653],{"class":207}," c",[187,655,577],{"class":229},[187,657,658],{"class":444}," data.categories ",[187,660,661],{"class":229},"??",[187,663,664],{"class":444}," []) cats.",[187,666,667],{"class":193},"add",[187,669,670],{"class":444},"(c);\n",[187,672,674],{"class":189,"line":673},9,[187,675,676],{"class":444},"}\n",[187,678,680],{"class":189,"line":679},10,[187,681,541],{"emptyLinePlaceholder":540},[187,683,685,687,690,692,695,698,701],{"class":189,"line":684},11,[187,686,617],{"class":229},[187,688,689],{"class":193}," mkdir",[187,691,586],{"class":444},[187,693,694],{"class":197},"'categories'",[187,696,697],{"class":444},", { recursive: ",[187,699,700],{"class":207},"true",[187,702,703],{"class":444}," });\n",[187,705,707,709,711,713,715,717],{"class":189,"line":706},12,[187,708,566],{"class":229},[187,710,569],{"class":444},[187,712,546],{"class":229},[187,714,653],{"class":207},[187,716,577],{"class":229},[187,718,719],{"class":444}," cats) {\n",[187,721,723,726,729,731,734,737,740],{"class":189,"line":722},13,[187,724,725],{"class":229},"  await",[187,727,728],{"class":193}," writeFile",[187,730,586],{"class":444},[187,732,733],{"class":197},"`categories\u002F${",[187,735,736],{"class":444},"c",[187,738,739],{"class":197},"}.md`",[187,741,742],{"class":444},",\n",[187,744,746,749,752,755,757,760,762,765,767,770,772,774,776,779,781,784,786,789,791,794],{"class":189,"line":745},14,[187,747,748],{"class":197},"    `---",[187,750,751],{"class":207},"\\n",[187,753,754],{"class":197},"layout: category",[187,756,751],{"class":207},[187,758,759],{"class":197},"title: ${",[187,761,736],{"class":444},[187,763,764],{"class":197},"}",[187,766,751],{"class":207},[187,768,769],{"class":197},"category: ${",[187,771,736],{"class":444},[187,773,764],{"class":197},[187,775,751],{"class":207},[187,777,778],{"class":197},"permalink: \u002Fcategories\u002F${",[187,780,736],{"class":444},[187,782,783],{"class":197},"}\u002F",[187,785,751],{"class":207},[187,787,788],{"class":197},"---",[187,790,751],{"class":207},[187,792,793],{"class":197},"`",[187,795,796],{"class":444},");\n",[187,798,800],{"class":189,"line":799},15,[187,801,676],{"class":444},[187,803,805,808,811,813,816,819,821,824,827],{"class":189,"line":804},16,[187,806,807],{"class":444},"console.",[187,809,810],{"class":193},"log",[187,812,586],{"class":444},[187,814,815],{"class":197},"`generated ${",[187,817,818],{"class":444},"cats",[187,820,25],{"class":197},[187,822,823],{"class":444},"size",[187,825,826],{"class":197},"} category stub(s)`",[187,828,796],{"class":444},[14,830,831,832,836],{},"This is the general escape hatch: any plugin that ",[833,834,835],"em",{},"generates files"," can become a script that generates the same files, committed to the repository. The site stays buildable by the classic Pages build, and the generation is visible in the diff — which is arguably better than a plugin nobody remembers configuring.",[27,838,840],{"id":839},"or-just-build-with-actions","Or Just Build With Actions",[14,842,843],{},"The allowlist exists because GitHub runs the classic build on its own infrastructure. Build it yourself and the restriction disappears:",[178,845,847],{"className":426,"code":846,"language":428,"meta":183,"style":183},"# .github\u002Fworkflows\u002Fpages.yml\nname: Deploy Jekyll\non:\n  push: { branches: [main] }\npermissions: { contents: read, pages: write, id-token: write }\nconcurrency: { group: pages, cancel-in-progress: false }\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: ruby\u002Fsetup-ruby@v1\n        with: { ruby-version: '3.3', bundler-cache: true }\n      - uses: actions\u002Fconfigure-pages@v5\n      - run: bundle exec jekyll build --baseurl \"${{ steps.pages.outputs.base_path }}\"\n        env: { JEKYLL_ENV: production }\n      - uses: actions\u002Fupload-pages-artifact@v3\n        with: { path: .\u002F_site }\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    environment: { name: github-pages, url: '${{ steps.deployment.outputs.page_url }}' }\n    steps:\n      - id: deployment\n        uses: actions\u002Fdeploy-pages@v4\n",[42,848,849,854,864,872,892,929,955,959,966,973,983,990,1003,1014,1040,1051,1063,1081,1093,1110,1115,1123,1134,1143,1170,1177,1189],{"__ignoreMap":183},[187,850,851],{"class":189,"line":190},[187,852,853],{"class":503},"# .github\u002Fworkflows\u002Fpages.yml\n",[187,855,856,859,861],{"class":189,"line":214},[187,857,858],{"class":440},"name",[187,860,445],{"class":444},[187,862,863],{"class":197},"Deploy Jekyll\n",[187,865,866,869],{"class":189,"line":250},[187,867,868],{"class":207},"on",[187,870,871],{"class":444},":\n",[187,873,874,877,880,883,886,889],{"class":189,"line":276},[187,875,876],{"class":440},"  push",[187,878,879],{"class":444},": { ",[187,881,882],{"class":440},"branches",[187,884,885],{"class":444},": [",[187,887,888],{"class":197},"main",[187,890,891],{"class":444},"] }\n",[187,893,894,897,899,902,904,907,909,912,914,917,919,922,924,926],{"class":189,"line":296},[187,895,896],{"class":440},"permissions",[187,898,879],{"class":444},[187,900,901],{"class":440},"contents",[187,903,445],{"class":444},[187,905,906],{"class":197},"read",[187,908,634],{"class":444},[187,910,911],{"class":440},"pages",[187,913,445],{"class":444},[187,915,916],{"class":197},"write",[187,918,634],{"class":444},[187,920,921],{"class":440},"id-token",[187,923,445],{"class":444},[187,925,916],{"class":197},[187,927,928],{"class":444}," }\n",[187,930,931,934,936,939,941,943,945,948,950,953],{"class":189,"line":382},[187,932,933],{"class":440},"concurrency",[187,935,879],{"class":444},[187,937,938],{"class":440},"group",[187,940,445],{"class":444},[187,942,911],{"class":197},[187,944,634],{"class":444},[187,946,947],{"class":440},"cancel-in-progress",[187,949,445],{"class":444},[187,951,952],{"class":207},"false",[187,954,928],{"class":444},[187,956,957],{"class":189,"line":388},[187,958,541],{"emptyLinePlaceholder":540},[187,960,961,964],{"class":189,"line":643},[187,962,963],{"class":440},"jobs",[187,965,871],{"class":444},[187,967,968,971],{"class":189,"line":673},[187,969,970],{"class":440},"  build",[187,972,871],{"class":444},[187,974,975,978,980],{"class":189,"line":679},[187,976,977],{"class":440},"    runs-on",[187,979,445],{"class":444},[187,981,982],{"class":197},"ubuntu-latest\n",[187,984,985,988],{"class":189,"line":684},[187,986,987],{"class":440},"    steps",[187,989,871],{"class":444},[187,991,992,995,998,1000],{"class":189,"line":706},[187,993,994],{"class":444},"      - ",[187,996,997],{"class":440},"uses",[187,999,445],{"class":444},[187,1001,1002],{"class":197},"actions\u002Fcheckout@v4\n",[187,1004,1005,1007,1009,1011],{"class":189,"line":722},[187,1006,994],{"class":444},[187,1008,997],{"class":440},[187,1010,445],{"class":444},[187,1012,1013],{"class":197},"ruby\u002Fsetup-ruby@v1\n",[187,1015,1016,1019,1021,1024,1026,1029,1031,1034,1036,1038],{"class":189,"line":745},[187,1017,1018],{"class":440},"        with",[187,1020,879],{"class":444},[187,1022,1023],{"class":440},"ruby-version",[187,1025,445],{"class":444},[187,1027,1028],{"class":197},"'3.3'",[187,1030,634],{"class":444},[187,1032,1033],{"class":440},"bundler-cache",[187,1035,445],{"class":444},[187,1037,700],{"class":207},[187,1039,928],{"class":444},[187,1041,1042,1044,1046,1048],{"class":189,"line":799},[187,1043,994],{"class":444},[187,1045,997],{"class":440},[187,1047,445],{"class":444},[187,1049,1050],{"class":197},"actions\u002Fconfigure-pages@v5\n",[187,1052,1053,1055,1058,1060],{"class":189,"line":804},[187,1054,994],{"class":444},[187,1056,1057],{"class":440},"run",[187,1059,445],{"class":444},[187,1061,1062],{"class":197},"bundle exec jekyll build --baseurl \"${{ steps.pages.outputs.base_path }}\"\n",[187,1064,1066,1069,1071,1074,1076,1079],{"class":189,"line":1065},17,[187,1067,1068],{"class":440},"        env",[187,1070,879],{"class":444},[187,1072,1073],{"class":440},"JEKYLL_ENV",[187,1075,445],{"class":444},[187,1077,1078],{"class":197},"production",[187,1080,928],{"class":444},[187,1082,1084,1086,1088,1090],{"class":189,"line":1083},18,[187,1085,994],{"class":444},[187,1087,997],{"class":440},[187,1089,445],{"class":444},[187,1091,1092],{"class":197},"actions\u002Fupload-pages-artifact@v3\n",[187,1094,1096,1098,1100,1103,1105,1108],{"class":189,"line":1095},19,[187,1097,1018],{"class":440},[187,1099,879],{"class":444},[187,1101,1102],{"class":440},"path",[187,1104,445],{"class":444},[187,1106,1107],{"class":197},".\u002F_site",[187,1109,928],{"class":444},[187,1111,1113],{"class":189,"line":1112},20,[187,1114,541],{"emptyLinePlaceholder":540},[187,1116,1118,1121],{"class":189,"line":1117},21,[187,1119,1120],{"class":440},"  deploy",[187,1122,871],{"class":444},[187,1124,1126,1129,1131],{"class":189,"line":1125},22,[187,1127,1128],{"class":440},"    needs",[187,1130,445],{"class":444},[187,1132,1133],{"class":197},"build\n",[187,1135,1137,1139,1141],{"class":189,"line":1136},23,[187,1138,977],{"class":440},[187,1140,445],{"class":444},[187,1142,982],{"class":197},[187,1144,1146,1149,1151,1153,1155,1158,1160,1163,1165,1168],{"class":189,"line":1145},24,[187,1147,1148],{"class":440},"    environment",[187,1150,879],{"class":444},[187,1152,858],{"class":440},[187,1154,445],{"class":444},[187,1156,1157],{"class":197},"github-pages",[187,1159,634],{"class":444},[187,1161,1162],{"class":440},"url",[187,1164,445],{"class":444},[187,1166,1167],{"class":197},"'${{ steps.deployment.outputs.page_url }}'",[187,1169,928],{"class":444},[187,1171,1173,1175],{"class":189,"line":1172},25,[187,1174,987],{"class":440},[187,1176,871],{"class":444},[187,1178,1180,1182,1185,1187],{"class":189,"line":1179},26,[187,1181,994],{"class":444},[187,1183,1184],{"class":440},"id",[187,1186,445],{"class":444},[187,1188,467],{"class":197},[187,1190,1192,1195,1197],{"class":189,"line":1191},27,[187,1193,1194],{"class":440},"        uses",[187,1196,445],{"class":444},[187,1198,1199],{"class":197},"actions\u002Fdeploy-pages@v4\n",[54,1201,1202,1324],{},[57,1203,65,1208,65,1211,65,1214,65,1217,65,1310],{"viewBox":1204,"role":60,"ariaLabelledBy":1205,"xmlns":64},"0 0 760 290",[1206,1207],"gp-routes-title","gp-routes-desc",[67,1209,1210],{"id":1206},"Classic Pages build versus an Actions build",[71,1212,1213],{"id":1207},"Two paths. The classic build takes a push straight to GitHub's own Jekyll build in safe mode, which allows only allowlisted gems and silently drops the rest. The Actions build runs your own Jekyll with any gems and any version, uploads the artifact, and deploys it to Pages.",[75,1215],{"x":77,"y":77,"width":78,"height":1216,"fill":80},"290",[82,1218,85,1219,85,1222,85,1228,85,1237,85,1243,85,1247,85,1250,85,1255,85,1259,85,1263,85,1267,85,1270,85,1272,85,1275,85,1278,85,1282,85,1284,85,1287,85,1305,65],{"style":84},[87,1220,1221],{"x":89,"y":90,"fill":91,"style":92},"One workflow file removes the whole constraint",[87,1223,1227],{"x":96,"y":1224,"fill":1225,"style":1226},"80","#a97b00","font-size:12px;font-weight:700","Classic",[75,1229],{"x":1230,"y":1231,"width":1232,"height":1233,"rx":1234,"fill":1235,"opacity":1236,"stroke":136,"style":104},"130","60","140","46","8","#d9e2ef","0.55",[87,1238,1242],{"x":1239,"y":1240,"fill":91,"style":1241},"200","88","font-size:12px;text-anchor:middle","git push",[75,1244],{"x":79,"y":1231,"width":1239,"height":1233,"rx":1234,"fill":1245,"opacity":1246,"stroke":1225,"style":104},"#ffca3a","0.3",[87,1248,1249],{"x":141,"y":1224,"fill":91,"style":1241},"GitHub builds, safe mode",[87,1251,1254],{"x":141,"y":1252,"fill":1225,"style":1253},"98","font-size:11px;text-anchor:middle","allowlisted gems only",[75,1256],{"x":1257,"y":1231,"width":1258,"height":1233,"rx":1234,"fill":1235,"opacity":1236,"stroke":136,"style":104},"530","190",[87,1260,1262],{"x":1261,"y":1240,"fill":91,"style":1241},"625","published",[87,1264,1266],{"x":96,"y":1265,"fill":103,"style":1226},"192","Actions",[75,1268],{"x":1230,"y":1269,"width":1232,"height":1233,"rx":1234,"fill":1235,"opacity":1236,"stroke":136,"style":104},"172",[87,1271,1242],{"x":1239,"y":1239,"fill":91,"style":1241},[75,1273],{"x":79,"y":1269,"width":1239,"height":1233,"rx":1234,"fill":101,"opacity":1274,"stroke":103,"style":104},"0.18",[87,1276,1277],{"x":141,"y":1265,"fill":91,"style":1241},"your build, your gems",[87,1279,1281],{"x":141,"y":1280,"fill":103,"style":1253},"210","any Jekyll version",[75,1283],{"x":1257,"y":1269,"width":1258,"height":1233,"rx":1234,"fill":1235,"opacity":1236,"stroke":136,"style":104},[87,1285,1286],{"x":1261,"y":1239,"fill":91,"style":1241},"artifact → published",[82,1288,1291,1292,1291,1296,1291,1299,1291,1302,85],{"stroke":136,"fill":1289,"style":1290},"none","stroke-width:2px","\n      ",[1102,1293],{"d":1294,"style":1295},"M272 83 L298 83","marker-end:url(#gp-arrow)",[1102,1297],{"d":1298,"style":1295},"M502 83 L528 83",[1102,1300],{"d":1301,"style":1295},"M272 195 L298 195",[1102,1303],{"d":1304,"style":1295},"M502 195 L528 195",[87,1306,1309],{"x":1307,"y":1308,"fill":136,"style":137},"26","264","The Actions route also gives you preview builds, content gates and a build log — none of which the classic build offers",[1311,1312,85,1313,65],"defs",{},[1314,1315,1291,1321,85],"marker",{"id":1316,"viewBox":1317,"refX":1234,"refY":1318,"markerWidth":1319,"markerHeight":1319,"orient":1320},"gp-arrow","0 0 10 10","5","7","auto-start-reverse",[1102,1322],{"d":1323,"fill":136},"M0 0 L10 5 L0 10 z",[171,1325,1326],{},"The classic build's only advantage is having no pipeline. Everything else — gems, versions, previews, checks — is on the other side of one workflow file.",[27,1328,1330],{"id":1329},"decide-plugin-by-plugin","Decide Plugin by Plugin",[14,1332,1333],{},"Before choosing a route, classify each plugin you use. The census takes ten minutes and usually shrinks the problem to two or three genuine cases.",[54,1335,1336,1408],{},[57,1337,65,1341,65,1344,65,1347,65,1349],{"viewBox":59,"role":60,"ariaLabelledBy":1338,"xmlns":64},[1339,1340],"gp-decide-title","gp-decide-desc",[67,1342,1343],{"id":1339},"Four outcomes for each plugin",[71,1345,1346],{"id":1340},"A decision matrix. If the plugin is allowlisted, keep it. If it transforms existing content, replace it with Liquid. If it generates files, replace it with a committed pre-build script. If it needs a gem or external tool at build time, that is the case that requires an Actions build.",[75,1348],{"x":77,"y":77,"width":78,"height":79,"fill":80},[82,1350,85,1351,85,1354,85,1358,85,1361,85,1365,85,1369,85,1372,85,1375,85,1378,85,1381,85,1385,85,1389,85,1393,85,1397,85,1399,85,1402,85,1405,65],{"style":84},[87,1352,1353],{"x":89,"y":90,"fill":91,"style":92},"Only the last box forces a decision about hosting",[75,1355],{"x":96,"y":97,"width":98,"height":1356,"rx":100,"fill":101,"opacity":1357,"stroke":103,"style":104},"96","0.16",[87,1359,1360],{"x":107,"y":108,"fill":91,"style":109},"Allowlisted → keep",[87,1362,1364],{"x":113,"y":1363,"fill":91,"style":115},"110","sitemap, feed, seo-tag, redirect-from",[87,1366,1368],{"x":113,"y":1367,"fill":136,"style":137},"134","no action needed at all",[75,1370],{"x":141,"y":97,"width":142,"height":1356,"rx":100,"fill":1371,"opacity":102,"stroke":1371,"style":104},"#1982c4",[87,1373,1374],{"x":148,"y":108,"fill":91,"style":109},"Transforms content → Liquid",[87,1376,1377],{"x":152,"y":1363,"fill":91,"style":115},"related posts, reading time, TOC",[87,1379,1380],{"x":152,"y":1367,"fill":136,"style":137},"a few lines in a layout",[75,1382],{"x":96,"y":1383,"width":98,"height":1356,"rx":100,"fill":1245,"opacity":1384,"stroke":1225,"style":104},"168","0.28",[87,1386,1388],{"x":107,"y":1387,"fill":91,"style":109},"194","Generates files → script",[87,1390,1392],{"x":113,"y":1391,"fill":91,"style":115},"222","category stubs, archive pages",[87,1394,1396],{"x":113,"y":1395,"fill":136,"style":137},"246","run it, commit the output",[75,1398],{"x":141,"y":1383,"width":142,"height":1356,"rx":100,"fill":143,"opacity":102,"stroke":145,"style":104},[87,1400,1401],{"x":148,"y":1387,"fill":91,"style":109},"Needs a gem → Actions",[87,1403,1404],{"x":152,"y":1391,"fill":91,"style":115},"image processing, asset pipelines",[87,1406,1407],{"x":152,"y":1395,"fill":136,"style":137},"the only genuinely blocking case",[171,1409,1410],{},"On the site measured below, eleven plugins classified as five allowlisted, three replaceable with Liquid, two script-generated and one that genuinely needed a gem — which was enough on its own to justify the Actions build.",[14,1412,1413,1414,1417],{},"Write the classification down in the repository next to the ",[42,1415,1416],{},"Gemfile",". It is the document that stops the same investigation happening again in a year, and it makes the eventual decision about hosting a one-line summary rather than an argument.",[27,1419,1421],{"id":1420},"measured-impact","Measured Impact",[14,1423,1424],{},"A 340-page documentation site that had been on the classic build for three years:",[1426,1427,1428,1444],"table",{},[1429,1430,1431],"thead",{},[1432,1433,1434,1438,1441],"tr",{},[1435,1436,1437],"th",{},"Measure",[1435,1439,1440],{},"Classic build",[1435,1442,1443],{},"Actions build",[1445,1446,1447,1459,1470,1481,1492],"tbody",{},[1432,1448,1449,1453,1456],{},[1450,1451,1452],"td",{},"Pages produced",[1450,1454,1455],{},"297",[1450,1457,1458],{},"340",[1432,1460,1461,1464,1467],{},[1450,1462,1463],{},"Plugins actually running",[1450,1465,1466],{},"5 of 11",[1450,1468,1469],{},"11 of 11",[1432,1471,1472,1475,1478],{},[1450,1473,1474],{},"Jekyll version",[1450,1476,1477],{},"pinned by Pages",[1450,1479,1480],{},"current",[1432,1482,1483,1486,1489],{},[1450,1484,1485],{},"Build time",[1450,1487,1488],{},"~35 s (opaque)",[1450,1490,1491],{},"48 s (visible log)",[1432,1493,1494,1497,1499],{},[1450,1495,1496],{},"Preview per pull request",[1450,1498,1289],{},[1450,1500,1501],{},"yes",[14,1503,1504],{},"The first row is the story: 43 category and archive pages were being generated locally, appearing in every local preview, and had never existed in production. Nobody had noticed because the pages were reachable from a navigation menu that was itself generated by the same missing plugin.",[27,1506,1508],{"id":1507},"keep-local-and-remote-in-agreement","Keep Local and Remote in Agreement",[14,1510,1511],{},"Whichever route you take, the second-largest source of surprise is a Jekyll version difference between your machine and the build. On the classic build, pin to the meta-gem so Bundler installs exactly what GitHub runs:",[178,1513,1517],{"className":1514,"code":1515,"language":1516,"meta":183,"style":183},"language-ruby shiki shiki-themes github-light github-dark","# Gemfile\nsource 'https:\u002F\u002Frubygems.org'\ngem 'github-pages', group: :jekyll_plugins\n","ruby",[42,1518,1519,1524,1529],{"__ignoreMap":183},[187,1520,1521],{"class":189,"line":190},[187,1522,1523],{},"# Gemfile\n",[187,1525,1526],{"class":189,"line":214},[187,1527,1528],{},"source 'https:\u002F\u002Frubygems.org'\n",[187,1530,1531],{"class":189,"line":250},[187,1532,1533],{},"gem 'github-pages', group: :jekyll_plugins\n",[14,1535,1536,1537,1540],{},"On the Actions route the equivalent discipline is pinning Ruby and committing ",[42,1538,1539],{},"Gemfile.lock",", so the runner installs the same dependency graph every time. Either way, the goal is that a build succeeding locally is evidence about the build that will run, rather than a coincidence.",[14,1542,1543],{},"Add a check that fails when the two diverge — comparing the page count from a local build against the live sitemap is crude, fast and catches the whole class of silent-drop problems:",[178,1545,1547],{"className":180,"code":1546,"language":182,"meta":183,"style":183},"LOCAL=$(find local-site -name '*.html' | wc -l)\nLIVE=$(curl -s https:\u002F\u002Fexample.github.io\u002Fsitemap.xml | grep -c '\u003Cloc>')\n[ \"$LOCAL\" -eq \"$LIVE\" ] || { echo \"page count differs: local $LOCAL, live $LIVE\"; exit 1; }\n",[42,1548,1549,1578,1605],{"__ignoreMap":183},[187,1550,1551,1554,1556,1559,1561,1563,1565,1567,1569,1572,1575],{"class":189,"line":190},[187,1552,1553],{"class":444},"LOCAL",[187,1555,609],{"class":229},[187,1557,1558],{"class":444},"$(",[187,1560,217],{"class":193},[187,1562,220],{"class":197},[187,1564,223],{"class":207},[187,1566,226],{"class":197},[187,1568,230],{"class":229},[187,1570,1571],{"class":193}," wc",[187,1573,1574],{"class":207}," -l",[187,1576,1577],{"class":444},")\n",[187,1579,1580,1583,1585,1587,1589,1591,1593,1595,1597,1600,1603],{"class":189,"line":214},[187,1581,1582],{"class":444},"LIVE",[187,1584,609],{"class":229},[187,1586,1558],{"class":444},[187,1588,253],{"class":193},[187,1590,256],{"class":207},[187,1592,259],{"class":197},[187,1594,230],{"class":229},[187,1596,264],{"class":193},[187,1598,1599],{"class":207}," -c",[187,1601,1602],{"class":197}," '\u003Cloc>'",[187,1604,1577],{"class":444},[187,1606,1607,1610,1613,1616,1618,1621,1624,1627,1629,1632,1635,1637,1640,1643,1645,1648,1650,1652,1655,1658,1661],{"class":189,"line":250},[187,1608,1609],{"class":444},"[ ",[187,1611,1612],{"class":197},"\"",[187,1614,1615],{"class":444},"$LOCAL",[187,1617,1612],{"class":197},[187,1619,1620],{"class":229}," -eq",[187,1622,1623],{"class":197}," \"",[187,1625,1626],{"class":444},"$LIVE",[187,1628,1612],{"class":197},[187,1630,1631],{"class":444}," ] ",[187,1633,1634],{"class":229},"||",[187,1636,600],{"class":444},[187,1638,1639],{"class":207},"echo",[187,1641,1642],{"class":197}," \"page count differs: local ",[187,1644,1615],{"class":444},[187,1646,1647],{"class":197},", live ",[187,1649,1626],{"class":444},[187,1651,1612],{"class":197},[187,1653,1654],{"class":444},"; ",[187,1656,1657],{"class":207},"exit",[187,1659,1660],{"class":207}," 1",[187,1662,1663],{"class":444},"; }\n",[27,1665,1667],{"id":1666},"pitfalls-rollback","Pitfalls & Rollback",[32,1669,1670,1676,1685,1691,1697,1706],{},[35,1671,1672,1675],{},[321,1673,1674],{},"Assuming a silent build is a correct build."," Diff local output against the live sitemap before trusting either.",[35,1677,1678,1681,1682,1684],{},[321,1679,1680],{},"Version drift."," Without the ",[42,1683,1157],{}," gem locally, your Jekyll differs from the server's and behaviour diverges quietly.",[35,1686,1687,1690],{},[321,1688,1689],{},"Committing generated files without a generator."," If a script produced them, keep the script in the repository or the next person cannot regenerate them.",[35,1692,1693,1696],{},[321,1694,1695],{},"Migrating hosting and dependencies at once."," Move to Actions first with the same gems, then change gems.",[35,1698,1699,1705],{},[321,1700,1701,1702,25],{},"Forgetting ",[42,1703,1704],{},"baseurl"," A project site served from a subpath needs it set correctly or every asset 404s.",[35,1707,1708,1711],{},[321,1709,1710],{},"Rollback:"," the classic build is still there. Deleting the workflow and re-enabling branch-based publishing restores the previous behaviour — including, unfortunately, the missing pages.",[27,1713,1715],{"id":1714},"conclusion","Conclusion",[14,1717,1718,1719,25],{},"Safe mode's allowlist is a constraint worth understanding rather than fighting: Liquid covers most content transformations, and a committed pre-build script covers file generation. But if you rely on more than a couple of plugins, the Actions route is a single workflow file that removes the constraint and adds previews, build logs and content gates in the process. Start by diffing local against live — the gap is usually larger than expected. The wider ecosystem picture is in ",[21,1720,24],{"href":23},[27,1722,1724],{"id":1723},"faq","FAQ",[1726,1727,1729],"h3",{"id":1728},"which-plugins-does-github-pages-actually-allow","Which plugins does GitHub Pages actually allow?",[14,1731,1732],{},"A short allowlist maintained by GitHub, covering sitemap generation, SEO tags, feeds, redirects, mentions, gists and a handful of others. Anything outside it is ignored silently on the classic Pages build, which is why a site can work locally and be missing content when deployed.",[1726,1734,1736],{"id":1735},"why-does-my-plugin-work-locally-but-not-on-github-pages","Why does my plugin work locally but not on GitHub Pages?",[14,1738,1739],{},"Because the classic Pages build runs in safe mode, which loads only allowlisted gems. Your local build has no such restriction, so a custom generator or a third-party gem runs happily on your machine and is skipped entirely on the server.",[1726,1741,1743],{"id":1742},"should-i-just-use-github-actions-instead","Should I just use GitHub Actions instead?",[14,1745,1746],{},"For most sites, yes. Building with Actions and deploying the output removes the allowlist entirely, gives you any gem and any Jekyll version, and costs a workflow file. Stay on the classic build only when you value having no pipeline at all.",[1726,1748,1750],{"id":1749},"can-liquid-really-replace-a-plugin","Can Liquid really replace a plugin?",[14,1752,1753],{},"Often, for anything that transforms content you already have — grouping, filtering, related lists, tables of contents, reading time. It cannot replace plugins that fetch data or invoke external tools, which is where a pre-build script comes in.",[1726,1755,1757],{"id":1756},"what-is-the-safest-migration-path-off-the-allowlist","What is the safest migration path off the allowlist?",[14,1759,1760],{},"Move to a GitHub Actions build first, changing nothing else. Once the build is yours, add or remove gems deliberately. Changing hosting and dependencies at the same time makes every failure ambiguous.",[27,1762,1764],{"id":1763},"related","Related",[32,1766,1767,1776,1783,1790,1797],{},[35,1768,1769,1772,1773,1775],{},[321,1770,1771],{},"Parent:"," ",[21,1774,24],{"href":23}," — what the plugins do and which are worth keeping.",[35,1777,1778,1782],{},[21,1779,1781],{"href":1780},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy\u002F","Replacing Jekyll Plugins When Migrating to Eleventy"," — the other way out of the allowlist.",[35,1784,1785,1789],{},[21,1786,1788],{"href":1787},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo\u002F","Migrating a Docs Site From Jekyll to Hugo"," — if you are leaving Jekyll entirely.",[35,1791,1792,1796],{},[21,1793,1795],{"href":1794},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002F","GitHub Actions for Automated SSG Builds"," — the pipeline the Actions route gives you.",[35,1798,1799,1803],{},[21,1800,1802],{"href":1801},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs\u002F","Eleventy vs Jekyll for Markdown-Heavy Blogs"," — the comparison behind the migration question.",[1805,1806,1807],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":183,"searchDepth":214,"depth":214,"links":1809},[1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1827],{"id":29,"depth":214,"text":30},{"id":51,"depth":214,"text":52},{"id":313,"depth":214,"text":314},{"id":487,"depth":214,"text":488},{"id":839,"depth":214,"text":840},{"id":1329,"depth":214,"text":1330},{"id":1420,"depth":214,"text":1421},{"id":1507,"depth":214,"text":1508},{"id":1666,"depth":214,"text":1667},{"id":1714,"depth":214,"text":1715},{"id":1723,"depth":214,"text":1724,"children":1821},[1822,1823,1824,1825,1826],{"id":1728,"depth":250,"text":1729},{"id":1735,"depth":250,"text":1736},{"id":1742,"depth":250,"text":1743},{"id":1749,"depth":250,"text":1750},{"id":1756,"depth":250,"text":1757},{"id":1763,"depth":214,"text":1764},[1829,1832,1835,1836],{"name":1830,"item":1831},"Home","\u002F",{"name":1833,"item":1834},"Choosing the Right Static Site Generator for Production","\u002Fchoosing-the-right-static-site-generator-for-production\u002F",{"name":24,"item":23},{"name":5,"item":1837},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins\u002F","2026-08-01","The allowlist blocks most gems. Replace plugin behaviour with Liquid, data files and a build step — or take the Actions route and keep the plugins you actually need.","md",[1842,1843,1844,1845,1846],{"q":1729,"a":1732},{"q":1736,"a":1739},{"q":1743,"a":1746},{"q":1750,"a":1753},{"q":1757,"a":1760},{},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins",{"title":5,"description":1839},"choosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins\u002Findex","article","y3mCCSJhAXX418vuFEsjLHy2ZF7nlcOyEXQtr2iuxF4",[1854,1857,1860,1863,1866,1869,1872,1875,1877,1879,1881,1883,1884,1887,1890,1892,1895,1898,1901,1904,1907,1910,1913,1916,1919,1922,1925,1928,1931,1934,1937,1940,1943,1946,1949,1952,1955,1958,1961,1964,1967,1970,1973,1976,1979,1982,1985,1988,1991,1994,1997,2000,2003,2006,2009,2012,2015,2018,2021,2024,2027,2030,2033,2036,2039,2042,2044,2047,2050,2053,2056,2059,2062,2065,2068,2071,2074,2077,2080,2083,2086,2089,2092],{"path":1855,"title":1856},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1858,"title":1859},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1861,"title":1862},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1864,"title":1865},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1867,"title":1868},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1870,"title":1871},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1873,"title":1874},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1876,"title":1833},"\u002Fchoosing-the-right-static-site-generator-for-production",{"path":1878,"title":1802},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs",{"path":1880,"title":24},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem",{"path":1882,"title":1781},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy",{"path":1848,"title":5},{"path":1885,"title":1886},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1888,"title":1889},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1891,"title":1788},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo",{"path":1893,"title":1894},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1896,"title":1897},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1899,"title":1900},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1902,"title":1903},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1905,"title":1906},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1908,"title":1909},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1911,"title":1912},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1914,"title":1915},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1917,"title":1918},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1920,"title":1921},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1923,"title":1924},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1926,"title":1927},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1929,"title":1930},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1932,"title":1933},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1935,"title":1936},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1938,"title":1939},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1941,"title":1942},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1944,"title":1945},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1947,"title":1948},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1950,"title":1951},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1953,"title":1954},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1956,"title":1957},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1959,"title":1960},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":1962,"title":1963},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1965,"title":1966},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1968,"title":1969},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1971,"title":1972},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1974,"title":1975},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1977,"title":1978},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1980,"title":1981},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1983,"title":1984},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1986,"title":1987},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1989,"title":1990},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1992,"title":1993},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1995,"title":1996},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1998,"title":1999},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":2001,"title":2002},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":2004,"title":2005},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":2007,"title":2008},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":2010,"title":2011},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":2013,"title":2014},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":2016,"title":2017},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":2019,"title":2020},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":2022,"title":2023},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":2025,"title":2026},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":2028,"title":2029},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":2031,"title":2032},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":2034,"title":2035},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":2037,"title":2038},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":2040,"title":2041},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":2043,"title":1795},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds",{"path":2045,"title":2046},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":2048,"title":2049},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":2051,"title":2052},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":2054,"title":2055},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":2057,"title":2058},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":2060,"title":2061},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":2063,"title":2064},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":2066,"title":2067},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":2069,"title":2070},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":2072,"title":2073},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":2075,"title":2076},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":2078,"title":2079},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":2081,"title":2082},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":2084,"title":2085},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":2087,"title":2088},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":2090,"title":2091},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":2093,"title":2094},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",1785611671170]