<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/assets/css/rss-feed-v1.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://www.simoncox.com/eleventy/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>11ty from SimonCox.com</title>
    <link>https://www.simoncox.com/eleventy/</link>
    <atom:link href="https://www.simoncox.com/eleventy/feed-11ty.xml" rel="self" type="application/rss+xml" />
    <description>Simon Cox&#39;s 11ty feed</description>
    <language>en</language>
    <item>
      <title>Alternate main heading and title in 11ty templates</title>
      <link>https://www.simoncox.com/short-articles/2026-01-17-alternate-main-heading-and-title-in-11ty-templates/</link>
      <description>&lt;h2 id=&quot;11ty-templates-set-up&quot; tabindex=&quot;-1&quot;&gt;11ty templates set up&lt;/h2&gt;
&lt;p&gt;In my 11ty templates I generally use the same content for the title and the h1 tags. In the frontmatter I always have a title field as the title tag is actually the only required html tag in a html document, did you know that? So it makes sense to have the title tag as a requirement in my frontmatter.&lt;/p&gt;
&lt;p&gt;With the h1 headings you can have as many a you like, technically, but I think this can be confusing to both humans and machines when trying to understand a page&#39;s content. The argument has long been that if you have a list of articles you can use the h1 tag for the articles title in that list.&lt;/p&gt;
&lt;p&gt;My reply to that is that a page with a list of articles should have the article titles as h2s and only one h1 at the top of the page to let the user know what is on the page.&lt;/p&gt;
&lt;h2 id=&quot;seo-practicalities-for-title-and-headings&quot; tabindex=&quot;-1&quot;&gt;SEO practicalities for title and headings&lt;/h2&gt;
&lt;p&gt;There is absolutely nothing wrong with having the same title and h1 on a page as far as SEO goes and this is often the easiest to set up as you just repeat the frontmatter title as the h1.&lt;/p&gt;
&lt;p&gt;Yes it is potentially not optimal, as having differing titles and h1&#39;s can cover more ground. This still will not mean overnight success and stardom for most sites. It could tip the balance in your favour in some cases though.&lt;/p&gt;
&lt;h2 id=&quot;11ty-templates-title-set-up&quot; tabindex=&quot;-1&quot;&gt;11ty templates title set up&lt;/h2&gt;
&lt;p&gt;Here is the title tag in my base template for the site:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;title&amp;gt;{{ title }} | Simon Cox&amp;lt;/title&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This picks up the title data from the front matter and adds a pipe and Simon Cox to the end of it for the title. That&#39;s not best practice if you are concerned about title length in the search engine results pages - though that&#39;s becoming less of an issue these days for other reasons - so you shoudl consider if you need your brand on the end of your titles or not.&lt;/p&gt;
&lt;p&gt;Here is my article template for the title and the h1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{ title }}&amp;lt;/h1&amp;gt;
	{{ content | safe }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This picks up the title frontmatter data and inserts it into the h1 tag and then the rest of the content I have in the markdown file follows.&lt;/p&gt;
&lt;h2 id=&quot;optimising-the-h1-for-seo&quot; tabindex=&quot;-1&quot;&gt;Optimising the h1 for seo&lt;/h2&gt;
&lt;p&gt;So having said that it doesn&#39;t matter if the title and the h1 are the same, there are indeed occasions where I don&#39;t want exactly the same title and h1 content. My favoured way of approaching this is to add h1 content in the frontmatter, usually the row after the title so that I can see what&#39;s going on when I return to the page to fix typos and rewrite the content as I realise my writing can be rubbish at times (like exactly now, to be extra meta about it).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
---
title: Alternate main heading and title in 11ty templates 
h1: Having a different title and h1 text using templating in 11ty
---

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have set up a conditional in the 11ty article template - note that I use nunjucks for my templates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{% if h1 %}{{h1}}{% else %}{{ title }}{% endif %}&amp;lt;/h1&amp;gt;
	{{ content | safe }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there is a h1 frontmatter data entry then it will be input into the h1 tag. If there isn&#39;t one then the template defaults to using the title - simple and easy to manage.&lt;/p&gt;
&lt;p&gt;This way if I want to add a different h1 then I can add it to the frontmatter, build and deploy.&lt;/p&gt;
&lt;p&gt;If you look at the source code of this page you will find that the title and h1 are indeed different!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Sat, 17 Jan 2026 24:00:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2026-01-17-alternate-main-heading-and-title-in-11ty-templates/</guid>
    </item>
    <item>
      <title>Creating a modular approach to json schema in an eleventy website</title>
      <link>https://www.simoncox.com/post/2025-10-28-creating-a-modular-approach-to-json-schema-in-an-eleventy-website/</link>
      <description>&lt;p&gt;Having enjoyed  &lt;a href=&quot;https://11tymeetup.dev/events/ep-25-structured-data-and-deploying-11ty/&quot;&gt;Kaj Kandler’s presentation at the 25th 11ty meetup July 2025&lt;/a&gt; I wanted a modular and automated way to add json schema to my personal website so built a set of templates and some simple logic.&lt;/p&gt;
&lt;h2 id=&quot;schema-files-structure-in-11ty&quot; tabindex=&quot;-1&quot;&gt;Schema files Structure in 11ty&lt;/h2&gt;
&lt;p&gt;I have created a schema directory in the includes directory and keep all the files associated with this in there.&lt;/p&gt;
&lt;p&gt;This has a main schema.njk file that is pulled into my base template which I use for all pages.
&lt;code&gt;src/_includes/base.njk&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In the base.njk I have this following include after the pages footer and before the body closes.
&lt;code&gt;{%- include &amp;quot;schema/schema.njk&amp;quot; -%}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-schema.njk-file&quot; tabindex=&quot;-1&quot;&gt;The schema.njk file&lt;/h2&gt;
&lt;p&gt;The file that pulls the schema all together is&lt;/p&gt;
&lt;p&gt;The full schema file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- set graph = [] %}



{# Include SiteNavigationElement #}

{%- set sitenavSchema %}{%- include &amp;quot;schema/SiteNavigationElement.njk&amp;quot; %}{%- endset %}



{# Include base fragments #}

{%- set websiteSchema %}{%- include &amp;quot;schema/website.njk&amp;quot; %}{%- endset %}

{%- set personSchema %}{%- include &amp;quot;schema/person.njk&amp;quot; %}{%- endset %}

{%- set organizationSchema %}{%- include &amp;quot;schema/organization.njk&amp;quot; %}{%- endset %}

{%- set webpageSchema %}{%- include &amp;quot;schema/webpage.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([sitenavSchema,websiteSchema, personSchema, organizationSchema, webpageSchema]) %}

	

{# Conditionally include article or blogpost schemas #}

{%- if page.url.startsWith(&amp;quot;/post/&amp;quot;) %}

{%- set articleSchema %}{%- include &amp;quot;schema/article.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([articleSchema]) %}

{%- elif page.url.startsWith(&amp;quot;/short-articles/&amp;quot;) %}

{%- set blogpostSchema %}{%- include &amp;quot;schema/blogpost.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([blogpostSchema]) %}

{%- endif %}

	

{# CollectionPage for /post/, /short-articles/, or /narrow-gauge-modelling/ list pages #}

{%- if page.url == &amp;quot;/post/&amp;quot; %}

{%- set collectionPostSchema %}{%- include &amp;quot;schema/collection-post.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([collectionPostSchema]) %}

{%- elif page.url == &amp;quot;/short-articles/&amp;quot; %}

{%- set collectionShortsSchema %}{%- include &amp;quot;schema/collection-shorts.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([collectionShortsSchema]) %}

{%- elif page.url == &amp;quot;/narrow-gauge-modelling/&amp;quot; %}

{%- set collectionNGMSchema %}{%- include &amp;quot;schema/collection-ngm.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([collectionNGMSchema]) %}

{%- endif %}

	

{# Breadcrumb #}

{%- set breadcrumbSchema %}{%- include &amp;quot;schema/breadcrumb.njk&amp;quot; %}{%- endset %}

{%- set graph = graph.concat([breadcrumbSchema]) %}

	

&amp;lt;script type=&amp;quot;application/ld+json&amp;quot;&amp;gt;

{

&amp;quot;@context&amp;quot;: &amp;quot;https://schema.org&amp;quot;,

&amp;quot;@graph&amp;quot;: [

{{ graph | join(&amp;quot;,&#92;n&amp;quot;) | safe }}

]

}

&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;initial-set-up&quot; tabindex=&quot;-1&quot;&gt;Initial set up&lt;/h3&gt;
&lt;p&gt;The file starts with an initial set up&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- set graph = [] %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates an empty array called graph that will hold all the schema objects.&lt;/p&gt;
&lt;h3 id=&quot;including-base-schema-components&quot; tabindex=&quot;-1&quot;&gt;Including Base Schema Components&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{# Include SiteNavigationElement #}
{%- set sitenavSchema %}{%- include &amp;quot;schema/SiteNavigationElement.njk&amp;quot; %}{%- endset %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern (repeated for multiple schemas) does three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;{%- set sitenavSchema %}...{%- endset %} captures the output of the included template into a variable&lt;/li&gt;
&lt;li&gt;{%- include &amp;quot;schema/SiteNavigationElement.njk&amp;quot; %} includes the contents of an external template file&lt;/li&gt;
&lt;li&gt;The - in  {%-  removes whitespace before/after the tag for cleaner output&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The same pattern is then repeated for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;websiteSchema - Website information&lt;/li&gt;
&lt;li&gt;personSchema - Person/author information&lt;/li&gt;
&lt;li&gt;organizationSchema - Organization/company information&lt;/li&gt;
&lt;li&gt;webpageSchema - Basic webpage information&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{%- set graph = graph.concat([sitenavSchema,websiteSchema, personSchema, organizationSchema, webpageSchema]) %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This adds all the base schemas to the graph array.&lt;/p&gt;
&lt;h3 id=&quot;conditional-article%2Fblog-post-schemas&quot; tabindex=&quot;-1&quot;&gt;Conditional Article/Blog Post Schemas&lt;/h3&gt;
&lt;p&gt;Then I start setting up schemas for types of page. All my article pages are in the post directory so anything in there will need the article schema.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- if page.url.startsWith(&amp;quot;/post/&amp;quot;) %}
  {%- set articleSchema %}{%- include &amp;quot;schema/article.njk&amp;quot; %}{%- endset %}
  {%- set graph = graph.concat([articleSchema]) %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and for the Shorticles in short-articles i decided to use a blog schema instead of article.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- elif page.url.startsWith(&amp;quot;/short-articles/&amp;quot;) %}
  {%- set blogpostSchema %}{%- include &amp;quot;schema/blogpost.njk&amp;quot; %}{%- endset %}
  {%- set graph = graph.concat([blogpostSchema]) %}
{%- endif %}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;collection-page-schemas&quot; tabindex=&quot;-1&quot;&gt;Collection Page Schemas&lt;/h3&gt;
&lt;p&gt;For the top level collection pages of lists - such as the articles list and shorticles list pages I have set a collections schema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- if page.url == &amp;quot;/post/&amp;quot; %}
  {%- set collectionPostSchema %}{%- include &amp;quot;schema/collection-post.njk&amp;quot; %}{%- endset %}
  {%- set graph = graph.concat([collectionPostSchema]) %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These conditions check for exact URL matches to collection/listing pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/post/ - Main posts listing page&lt;/li&gt;
&lt;li&gt;/short-articles/ - Short articles listing page&lt;/li&gt;
&lt;li&gt;/narrow-gauge-modelling/ - A specific category listing page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each gets its own collection schema added to the graph.&lt;/p&gt;
&lt;h3 id=&quot;breadcrumb-schema&quot; tabindex=&quot;-1&quot;&gt;Breadcrumb Schema&lt;/h3&gt;
&lt;p&gt;Breadcrumb schema is a tricky one to fathom and the logic is in the breadcrumb.njk file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- set breadcrumbSchema %}{%- include &amp;quot;schema/breadcrumb.njk&amp;quot; %}{%- endset %}
{%- set graph = graph.concat([breadcrumbSchema]) %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This adds breadcrumb navigation schema to every page.&lt;/p&gt;
&lt;h3 id=&quot;final-json-ld-output&quot; tabindex=&quot;-1&quot;&gt;Final JSON-LD Output&lt;/h3&gt;
&lt;p&gt;Finally it pulls together the elements into a JSON-LD script&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;application/ld+json&amp;quot;&amp;gt;
{
  &amp;quot;@context&amp;quot;: &amp;quot;https://schema.org&amp;quot;,
  &amp;quot;@graph&amp;quot;: [
    {{ graph | join(&amp;quot;,&#92;n&amp;quot;) | safe }}
  ]
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Creates a script tag with type=&amp;quot;application/ld+json&amp;quot; (tells search engines this is structured data)&lt;/li&gt;
&lt;li&gt;Sets the Schema.org context&lt;/li&gt;
&lt;li&gt;Creates a @graph array containing all the collected schemas&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{ graph | join(&amp;quot;,&#92;n&amp;quot;) | safe }}&lt;/code&gt; joins all array elements with commas and newlines, and marks it as safe HTML (won&#39;t be escaped)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is a single script tag containing all relevant structured data for the current page, helping search engines understand the content and potentially enabling rich snippets in search results.&lt;/p&gt;
&lt;h2 id=&quot;individual-schema-files&quot; tabindex=&quot;-1&quot;&gt;Individual Schema files&lt;/h2&gt;
&lt;p&gt;The individual schema types have their own file in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/_includes/schema/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which contains these files&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;article.njk
blogpost.njk
breadcrumb.njk
collection-post.njk
collection-shorts.njk
contactpage.njk
copyright.njk
faqpage.njk
imageobject.njk
organisation.njk
person.njk
schema.njk – the main file
webpage.njk
website.njk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of these files created the schema JSON so more can be added as you require. I have listed the code for a couple below to give you the general idea of how they go together.&lt;/p&gt;
&lt;h3 id=&quot;article-schema-file&quot; tabindex=&quot;-1&quot;&gt;Article schema file&lt;/h3&gt;
&lt;p&gt;The article.njk schema file in full&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{

&amp;quot;@type&amp;quot;: &amp;quot;Article&amp;quot;,

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}{{ page.url }}#article&amp;quot;,

&amp;quot;headline&amp;quot;: &amp;quot;{{ title }}&amp;quot;,

&amp;quot;datePublished&amp;quot;: &amp;quot;{{ page.date.toISOString() }}&amp;quot;,

&amp;quot;dateModified&amp;quot;: {% if dateUpdated %}&amp;quot;{{ dateUpdated.toISOString() }}&amp;quot;{% else %}&amp;quot;{{ page.date.toISOString() }}&amp;quot;{% endif %},

&amp;quot;image&amp;quot;: {

&amp;quot;@type&amp;quot;: &amp;quot;ImageObject&amp;quot;,

&amp;quot;url&amp;quot;: &amp;quot;{{ site.url }}{{ image }}&amp;quot;,

&amp;quot;inLanguage&amp;quot;: &amp;quot;en-GB&amp;quot;

},

&amp;quot;author&amp;quot;: {

&amp;quot;@type&amp;quot;: &amp;quot;Person&amp;quot;,

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}/#person&amp;quot;,

&amp;quot;name&amp;quot;: &amp;quot;Simon Cox&amp;quot;

},

&amp;quot;mainEntityOfPage&amp;quot;: {

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}{{ page.url }}&amp;quot;

}

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I am pulling frontmatter data as well as global data - though top make it more transferrable items like name: Simon Cox could have used a placeholder instead of being hardcoded. i should clean that up so I can use this on other sites!&lt;/p&gt;
&lt;h3 id=&quot;the-collection-post-schema-file&quot; tabindex=&quot;-1&quot;&gt;The collection-post schema file&lt;/h3&gt;
&lt;p&gt;I&#39;m sure I could use some logic to simplify the number of files but this works for me at the moment.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{

&amp;quot;@type&amp;quot;: &amp;quot;CollectionPage&amp;quot;,

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}/post/#webpage&amp;quot;,

&amp;quot;url&amp;quot;: &amp;quot;{{ site.url }}/post/&amp;quot;,

&amp;quot;name&amp;quot;: &amp;quot;Articles&amp;quot;,

&amp;quot;description&amp;quot;: &amp;quot;A collection of in-depth articles by Simon Cox.&amp;quot;,

&amp;quot;isPartOf&amp;quot;: {

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}/#website&amp;quot;

},

&amp;quot;hasPart&amp;quot;: [

{%- for post in collections.Post -%}

{

&amp;quot;@type&amp;quot;: &amp;quot;Article&amp;quot;,

&amp;quot;@id&amp;quot;: &amp;quot;{{ site.url }}{{ post.url }}#webpage&amp;quot;

}{%- if not loop.last %},{% endif %}

{%- endfor -%}

]

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope that helps someone put together their own schema on an 11ty site!&lt;/p&gt;
&lt;p&gt;To check your progress you could install my &lt;a href=&quot;https://www.simoncox.com/simons-schema/&quot;&gt;schema chrome extension&lt;/a&gt;.&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content//assets/img/content/2025-10-28-creating-a-modular-approach-to-json-schema-in-an-eleventy-website.webp" type="image" />
      <pubDate>Tue, 28 Oct 2025 24:00:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/post/2025-10-28-creating-a-modular-approach-to-json-schema-in-an-eleventy-website/</guid>
    </item>
    <item>
      <title>Creating Cloudflare redirects</title>
      <link>https://www.simoncox.com/short-articles/2025-10-08-creating-cloudflare-redirects/</link>
      <description>&lt;p&gt;I was having issues sorting out the redirects on Cloudflare and have learnt a few things on the way so am documenting them here – because I will forget it all whilst in the pursuit of happier things.&lt;/p&gt;
&lt;h2 id=&quot;directory-migration&quot; tabindex=&quot;-1&quot;&gt;Directory migration&lt;/h2&gt;
&lt;p&gt;I made a major change to the structure of a section of my site /shorts/ has reverted back to &lt;a href=&quot;https://www.simoncox.com/short-articles/&quot;&gt;/short-articles/&lt;/a&gt; as I am testing out a possible issue with &lt;a href=&quot;https://www.simoncox.com/post/2025-04-07-recovery-from-a-bing-de-indexing/&quot;&gt;this sites current  lack of SERP visibility in Bing&lt;/a&gt;. As such I needed to revert the redirects from /short-articles/ to /shorts/ back again. Not usually an issue for my skills but I thought I&#39;d make some notes.&lt;/p&gt;
&lt;h2 id=&quot;cloudflare-hosting&quot; tabindex=&quot;-1&quot;&gt;Cloudflare hosting&lt;/h2&gt;
&lt;p&gt;This site is currently hosted on Cloudflare pages, auto deployed from its github repository. I can use the inbuilt redirect solution that Cloudflare provides but that has a very limited capacity so I have created a file named _redirects that Cloudflare recognises (as does Netlify) and uses for redirects. This is in my 11ty setup and has a passthrough so that it is output in the build exactly the same as I have written it.&lt;/p&gt;
&lt;p&gt;This file can have up to 2,000 redirects if properly formatted. I have had issues with only being able to use 120 redirects but this was because I had built some of them incorrectly.&lt;/p&gt;
&lt;h3 id=&quot;deployment-logs&quot; tabindex=&quot;-1&quot;&gt;Deployment logs&lt;/h3&gt;
&lt;p&gt;One source of info that is really useful is the deployment log files. Find it here:
Cloudflare &amp;gt; Compute (Workers) &amp;gt; your site &amp;gt; View details&lt;/p&gt;
&lt;p&gt;Now scroll down in the Build log and it will tell you all sorts of interesting things such as time and what happened. This includes parsing the redirects file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10:41:26.990	Parsed 83 valid redirect rules.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The log helpfully tells you redirects that have not been implemented and also provides some optimisation suggestions such as the order of the redirects.&lt;/p&gt;
&lt;h2 id=&quot;notes-on-cloudflare-redirect-rules-best-practices&quot; tabindex=&quot;-1&quot;&gt;Notes on Cloudflare redirect rules best practices&lt;/h2&gt;
&lt;h3 id=&quot;exclamation-mark-on-the-end-of-the-rule&quot; tabindex=&quot;-1&quot;&gt;Exclamation mark on the end of the rule&lt;/h3&gt;
&lt;p&gt;I think this was a leftover from Netlify&#39;s redirect rules&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://simoncox.com/*   https://www.simoncox.com/:splat  301!
Valid status codes are 200, 301, 302 (default), 303, 307, or 308. Got 301!.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did a find and replace and removed the exclamations marks.&lt;/p&gt;
&lt;p&gt;There was another issue with this particular rule though!&lt;/p&gt;
&lt;h3 id=&quot;only-relative-urls-are-allowed&quot; tabindex=&quot;-1&quot;&gt;Only relative URLs are allowed&lt;/h3&gt;
&lt;p&gt;I had some domain redirects in place, &lt;a href=&quot;https://httpscolonforwardslashforwardslash.com/&quot;&gt;because you can&#39;t have enough domains&lt;/a&gt;, but Cloudflare doesn&#39;t like them as the source URL, they have to be a part of the directory structure, so I had to take them out.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://www.tagsoup.net/*  https://www.simoncox.com/:splat  301!
Only relative URLs are allowed. Skipping absolute URL https://www.tagsoup.net/*.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I am now forwarding those domains using an external method.&lt;/p&gt;
&lt;h3 id=&quot;redirect-rule-spaces-and-or-tabs&quot; tabindex=&quot;-1&quot;&gt;Redirect rule spaces and or tabs&lt;/h3&gt;
&lt;p&gt;Cloudflare is quite particular about the number of spaces - it looks for gaps in the redirect rule of 2 and 3 sets of spaces or tabs. If there are 4 or more sets of spaces and the rule will be ignored.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/blog/category:Model Railways /narrow-gauge-modelling/ 301
Expected exactly 2 or 3 whitespace-separated tokens. Got 4.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means if your URL has a space you will need to use %20 in place of the space:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/blog/category:Model%20Railways /narrow-gauge-modelling/ 301
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;colons-interpreted-as-placeholders&quot; tabindex=&quot;-1&quot;&gt;Colons interpreted as placeholders&lt;/h3&gt;
&lt;p&gt;I have some URLs with colons in them, such as /blog/category:things, and have put these under section 2 as the colon is interpreted as a placeholder, or you can URL-encode the colon of course!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/blog/category%3AModel%20Railways /narrow-gauge-modelling/ 301
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;order-of-redirect-rules&quot; tabindex=&quot;-1&quot;&gt;Order of redirect rules&lt;/h3&gt;
&lt;p&gt;For performant reasons it is better to list static redirects before dynamic ones so that Cloudflare’s systems can run these more effectively:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Static rules - /home, /about, /contact&lt;/li&gt;
&lt;li&gt;Rules with placeholders - /blog/:slug, /users/:id&lt;/li&gt;
&lt;li&gt;Rules with splats - /old-site/*&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I split these up using # comments in my redirect files.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts-about-the-cloudflare-redirect-file-rules&quot; tabindex=&quot;-1&quot;&gt;Final thoughts about the Cloudflare redirect file rules&lt;/h2&gt;
&lt;p&gt;I originally thought I could switch the sites hosting between Cloudflare and Netlify hosting by just changing the DNS, a very handy and quick solution, but I am not going to have to keep two sets of redirect files if I want to do this as the rules are quite different between them.&lt;/p&gt;
&lt;p&gt;Having learnt more about the Cloudflare redirect rules If am much happier running the rules from this file. Also, this started off as a shorticle but is long enough to be a full article.&lt;/p&gt;
&lt;p&gt;You can read more on &lt;a href=&quot;https://developers.cloudflare.com/rules/url-forwarding/&quot;&gt;Cloudflare’s full documentation on redirects&lt;/a&gt; and let me know if I have missed anything important out!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Wed, 08 Oct 2025 01:00:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2025-10-08-creating-cloudflare-redirects/</guid>
    </item>
    <item>
      <title>Creating a blogroll page from Mastodon bookmarks</title>
      <link>https://www.simoncox.com/post/2025-04-04-creating-a-blogroll-page-from-mastodon-bookmarks/</link>
      <description>&lt;h2 id=&quot;why-did-i-want-a-bookmarks-page%3F&quot; tabindex=&quot;-1&quot;&gt;Why did I want a bookmarks page?&lt;/h2&gt;
&lt;p&gt;A thing that has been on &lt;a href=&quot;https://www.simoncox.com/ideas/&quot;&gt;my ideas list&lt;/a&gt; for quite some time is to create a links type page from the Toots I bookmark on Mastodon. The mastodon bookmarks are really handy for items that I want to go back to, to read more or as a prompt to get me to do something. Having them actually on my website makes it more like a blogroll of old and &lt;a href=&quot;https://www.simoncox.com/bookmarks/&quot;&gt;here is my bookmarks page&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;mastodon-api&quot; tabindex=&quot;-1&quot;&gt;Mastodon API&lt;/h2&gt;
&lt;p&gt;Mastodon has an API that you can pull many interesting things out of and this was what got me thinking about this idea in the first place. It has been on the ideas list for a few years because I can&#39;t really write Javascript. I can look through it and make some good assumptions to whats happening but coding this is still a dark art to me. The &lt;a href=&quot;https://docs.joinmastodon.org/methods/bookmarks/&quot;&gt;Mastodon API docs&lt;/a&gt; are available to peruse and I had a glance at one point.&lt;/p&gt;
&lt;h2 id=&quot;down-the-rabbit-hole-of-code-i-go&quot; tabindex=&quot;-1&quot;&gt;Down the rabbit hole of code I go&lt;/h2&gt;
&lt;p&gt;So I turned to AI. Of course I did, and if you think less of me for it then that&#39;s just fine with me and no doubt you will give me some grief on Mastodon. Off you toddle.&lt;/p&gt;
&lt;p&gt;Meanwhile for those of you that are still interested I went through a fair bit of prompting and trial and error to get this working in the first place and then how I wanted it to work. It wasn&#39;t 5 mins time of ChatGPT and there it was. No, it was a week of spare moments pulling out hair, trying to understand things and then trying again. In the end I got there and I did learn a lot from it.&lt;/p&gt;
&lt;p&gt;With the Mastodon API you can pull out 40 bookmarks in one go - you can pull out more but then you have to call the API again asking for pagination and I have not had time to work out how to do that properly yet. I may not ever bother as 40 takes me back quite a while as I don&#39;t bookmark that often, though I might do more of it now!&lt;/p&gt;
&lt;h3 id=&quot;the-environment-file&quot; tabindex=&quot;-1&quot;&gt;The environment file&lt;/h3&gt;
&lt;p&gt;First I had to set up a .env file with my Instance and Access token at root level in my 11ty folder so that the script to make the authenticated call. In this file I added the following two lines (I have not filled in the instance or token in this example!):
&lt;code&gt;MASTODON_INSTANCE=https://your.instance MASTODON_ACCESS_TOKEN=your_access_token&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;My instance being the Mastodon server I am on. The Mastodon Access Token I needed to create by going to my Mastodon Preferences &amp;gt; Development to create New application. I give the Application a name and then at the top of the page I found my access token - it&#39;s a long string. Next I choose the Scope - read:bookmarks is all that I needed for this and then I saved the application down at the bottom with the Save Changes button.&lt;/p&gt;
&lt;p&gt;I added those two values to my .env file.&lt;/p&gt;
&lt;h3 id=&quot;the-bookmarks-script&quot; tabindex=&quot;-1&quot;&gt;The bookmarks Script&lt;/h3&gt;
&lt;p&gt;Next I created a bookmarks.js file in my _data folder and this is my current script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Load environment variables from the .env file
require(&amp;quot;dotenv&amp;quot;).config();

// Import EleventyFetch for fetching remote data and caching it
const EleventyFetch = require(&amp;quot;@11ty/eleventy-fetch&amp;quot;);

// Get Mastodon instance URL and access token from environment variables
const MASTODON_INSTANCE = process.env.MASTODON_INSTANCE;
const MASTODON_ACCESS_TOKEN = process.env.MASTODON_ACCESS_TOKEN;

// Helper function to check if an image URL is valid (it will try to fetch only the header)
async function checkImageExists(url) {
  try {
    // Send a HEAD request to check if the image exists without downloading it
    const response = await fetch(url, { method: &amp;quot;HEAD&amp;quot; });
    return response.ok; // If the response status is 2xx, the image exists
  } catch (error) {
    // If there&#39;s an error (network issue, or image doesn&#39;t exist), return false
    return false;
  }
}

// Helper function to extract the &#39;next&#39; page URL from the Link header (used for pagination)
function getNextPage(linkHeader) {
  // If the link header is missing, there&#39;s no next page
  if (!linkHeader) return null;

  // Regular expression to extract the &#39;next&#39; link from the pagination header
  const match = linkHeader.match(/&amp;lt;([^&amp;gt;]+)&amp;gt;;&#92;s*rel=&amp;quot;next&amp;quot;/);
  return match ? match[1] : null; // Return the next page URL or null if not found
}

// Main function to fetch and process Mastodon bookmarks
module.exports = async function () {
  // Check if the required environment variables are set, otherwise log an error
  if (!MASTODON_INSTANCE || !MASTODON_ACCESS_TOKEN) {
    console.error(&amp;quot;Missing Mastodon credentials in .env file&amp;quot;);
    return []; // Return an empty array if credentials are missing
  }

  // Construct the URL to fetch bookmarks from Mastodon API
  const url = `${MASTODON_INSTANCE}/api/v1/bookmarks?limit=40`; // Fetch up to 40 bookmarks
  // Add the authorization header with the Mastodon access token
  const headers = { Authorization: `Bearer ${MASTODON_ACCESS_TOKEN}` };

  try {
    // Use EleventyFetch to fetch the bookmarks with caching for 12 hours
    const data = await EleventyFetch(url, {
      duration: &amp;quot;12h&amp;quot;,  // Cache the result for 12 hours
      type: &amp;quot;json&amp;quot;,     // Expect JSON data from the API
      fetchOptions: { headers } // Include the headers (with the access token)
    });

    // Process the fetched data (map over each bookmark and handle the media)
    return await Promise.all(
      data.map(async bookmark =&amp;gt; {
        // Check for images in the media_attachments array and validate them
        const images = await Promise.all(
          bookmark.media_attachments
            .filter(attachment =&amp;gt; attachment.type === &amp;quot;image&amp;quot;) // Filter only image attachments
            .map(async attachment =&amp;gt; {
              // For each image, check if it exists by using the checkImageExists function
              if (await checkImageExists(attachment.url)) {
                return attachment.url; // Return the image URL if valid
              }
              return null; // Return null if the image doesn&#39;t exist
            })
        );

        // Return a simplified structure for each bookmark with the relevant data
        return {
          id: bookmark.id,  // Bookmark ID
          content: bookmark.content,  // Bookmark content (text)
          url: bookmark.url,  // URL of the bookmarked post
          created_at: bookmark.created_at,  // Timestamp when the bookmark was created
          account: {
            username: bookmark.account.username,  // Account username
            url: bookmark.account.url,  // Account URL
            avatar: bookmark.account.avatar  // Account avatar URL
          },
          images: images.filter(img =&amp;gt; img !== null) // Remove null image entries
        };
      })
    );
  } catch (error) {
    // If there was an error fetching data, log it and return an empty array
    console.error(&amp;quot;Failed to fetch Mastodon bookmarks:&amp;quot;, error);
    return [];
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;page-code&quot; tabindex=&quot;-1&quot;&gt;Page code&lt;/h2&gt;
&lt;p&gt;To display the output I set up the following. I have the styles inline at the moment as I am still mucking around with it so they may well end up in the site css.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{% for bookmark in bookmarks %}
&amp;lt;section style=&amp;quot;border: 1px solid #ccc; padding: 10px; margin: 20px auto; border-radius: 20px;max-width:360px;overflow-wrap:anywhere;&amp;quot;&amp;gt;
	&amp;lt;img style=&amp;quot;width:40px;margin:0&amp;quot; src=&amp;quot;{{ bookmark.account.avatar }}&amp;quot; alt=&amp;quot;{{ bookmark.account.username }}&amp;quot; width=&amp;quot;40&amp;quot; height=&amp;quot;40&amp;quot;&amp;gt;
	&amp;lt;p&amp;gt;from {{ bookmark.account.username }}&amp;lt;/p&amp;gt;
	&amp;lt;p&amp;gt;{{ bookmark.content | safe }}&amp;lt;/p&amp;gt;
	{% if bookmark.images.length &amp;gt; 0 %}
	&amp;lt;section class=&amp;quot;images&amp;quot;&amp;gt;
		{% for image in bookmark.images %}
		&amp;lt;img src=&amp;quot;{{ image }}&amp;quot; alt=&amp;quot;Mastodon image&amp;quot; /&amp;gt;
		{% endfor %}
	&amp;lt;/section&amp;gt;
	{% endif %}
	&amp;lt;small&amp;gt;&amp;lt;a href=&amp;quot;{{ bookmark.url }}&amp;quot;&amp;gt;Link to the the Toot&amp;lt;/a&amp;gt; – Saved on {{ bookmark.created_at }}&amp;lt;/small&amp;gt;
&amp;lt;/section&amp;gt;
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;process-of-adding-the-bookmarks&quot; tabindex=&quot;-1&quot;&gt;Process of adding the bookmarks&lt;/h2&gt;
&lt;p&gt;I did not want this process to be client side as it would have taken too long to load the page and there was a bigger risk of exposing my token. So I have this setup to build out the page when I run my build command locally. the html page gets build and the images added from cache to the img folder. That is then submitted to Github and deployed to the server.&lt;/p&gt;
&lt;p&gt;The disadvantage with this is that I need to go through a build process to get the latest 40 bookmarks onto the page. I am always tinkering with this site though so that&#39;s not a problem for me!&lt;/p&gt;
&lt;h2 id=&quot;what-next%3F&quot; tabindex=&quot;-1&quot;&gt;What next?&lt;/h2&gt;
&lt;p&gt;Hopefully some others will find this useful and create something similar if not a lot better and I await for you to post on Mastodon so that I can bookmark them!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content//assets/img/content/2025-04-04-creating-a-blogroll-page-from-mastodon-bookmarks.webp" type="image" />
      <pubDate>Fri, 04 Apr 2025 01:00:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/post/2025-04-04-creating-a-blogroll-page-from-mastodon-bookmarks/</guid>
    </item>
    <item>
      <title>11ty Git Commit Messages</title>
      <link>https://www.simoncox.com/short-articles/2024-12-14-git-commit-messages/</link>
      <description>&lt;p&gt;Just read &lt;a href=&quot;https://block81.com/blog/git-commit-messages&quot;&gt;Git Commit Messages by Angie Herrera&lt;/a&gt; who has started organising Git Commit Messages &lt;em&gt;&amp;quot;in a way that keeps things organized and makes future me a bit happier and less stressed&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What a great idea!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I decided I am going to do the same – so I copied Angie&#39;s bullet points into a Note and then started thinking about it. Angie is a very accomplished Craft CMS developer, who I have been honoured to provide some SEO work, so the list makes perfect sense for a Craft build. However with 11ty builds I needed something a little more because I am now also making content changes in my 11ty builds.&lt;/p&gt;
&lt;p&gt;So here is my extended version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;feat:&lt;/strong&gt; anything feature related, even if deleting code or making changes to existing features&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fix:&lt;/strong&gt; fixing a bug or issue without new feature code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;refactor:&lt;/strong&gt; code change that neither fixes a bug nor adds a feature but somehow improves the codebase (for me this is usually streamlining CSS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cms:&lt;/strong&gt; changes to the CMS (version updates or plugin updates, installs, or removals)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;build:&lt;/strong&gt; Changes that affect the build system or external dependencies, such as npm, DDEV setup, or Laravel Mix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;content:&lt;/strong&gt; New content, images and typo fixes and corrections&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;seo:&lt;/strong&gt; Changes optimising code or content for seo purposes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That should see me right!&lt;/p&gt;
</description>
      
      <pubDate>Sat, 14 Dec 2024 11:49:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2024-12-14-git-commit-messages/</guid>
    </item>
    <item>
      <title>11ty upgrading node in netlify</title>
      <link>https://www.simoncox.com/short-articles/2024-10-15-11ty-upgrading-node-in-netlify/</link>
      <description>&lt;p&gt;I upgraded the image plugin for 11ty on a small site which had an unintended chain of issues!&lt;/p&gt;
&lt;p&gt;I noticed that the Source control had node elements in it,  even though I have node_modules in my gitignore file. After some investigation and testing I realised that I must have deployed the site before adding the gitignore. Really should not have node_modules deployed to the repository even though it is private.&lt;/p&gt;
&lt;p&gt;To remove the node_modules from the repository I moved the folder up a level and committed and synced the files. That removed the node_modules from the repository. I then moved the node_modules folder back into the local site and ran a build - all was ok.&lt;/p&gt;
&lt;p&gt;I checked Netlify and the build had failed.&lt;/p&gt;
&lt;p&gt;It turned out (i.e. head scratching and testing for a while) is wasn&#39;t my local Node but the Node version on Netlify that needed upgrading - of course it was.&lt;/p&gt;
&lt;p&gt;With the node_modules in the build, Netlify had used the node_modules I had provided to create the build - which is a great feature but not what was needed. As soon as I had removed them Netlify used the default node_modules which failed to build because apparently node needed upgrading.&lt;/p&gt;
&lt;p&gt;On the Netlify Site Configuration &amp;gt; Build &amp;amp; Deploy &amp;gt; Dependency management (scroll down) to find and configure the Netlify version of Node - it was 15 and I apparently needed 18, so set to 20. Bingo - built and deployed magnificently!&lt;/p&gt;
&lt;p&gt;I hope this note helps someone but it is really for me the next time I get this issue on another site and don&#39;t realise I fixed it before.&lt;/p&gt;
</description>
      
      <pubDate>Tue, 15 Oct 2024 12:45:29 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2024-10-15-11ty-upgrading-node-in-netlify/</guid>
    </item>
    <item>
      <title>11ty collections tag links</title>
      <link>https://www.simoncox.com/short-articles/2024-03-17-11ty-collection-tag-links/</link>
      <description>&lt;p&gt;More of a reminder for myself next time Image need to do this!&lt;/p&gt;
&lt;h2 id=&quot;adding-a-collection-tag-to-a-post-as-a-link&quot; tabindex=&quot;-1&quot;&gt;Adding a collection tag to a post as a link&lt;/h2&gt;
&lt;p&gt;There are some lovely ways to automate sets of tags with a list page. Hoever I don&#39;t think these can have additional content to make them into a proper topic hub page so I am building them out manually.&lt;/p&gt;
&lt;p&gt;For posts in my Articles and Shorts sections I have started adding tags to the modelling topic ones. After the author sign off with date, and update date if there is one, I have added links to the tag hub page.&lt;/p&gt;
&lt;p&gt;I started with this 11ty code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{% if tag == &amp;quot;Modelling&amp;quot; %}| &amp;lt;a href=&amp;quot;/narrow-gauge-modelling&amp;quot;&amp;gt;Modelling&amp;lt;/a&amp;gt;{% endif %}
{% if tag == &amp;quot;Loxley&amp;quot; %}| &amp;lt;a href=&amp;quot;/loxleybarton&amp;quot;&amp;gt;Loxley Barton Falls&amp;lt;/a&amp;gt;{% endif %} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it wasn&#39;t showing up the tag links.
Turns out that because I have multiple tags on some in this form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tags:
- Loxley
- Modelling
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out that 11ty concatenates them as a string, of course, so I need to cycle through that and pull out the tags that I need.&lt;/p&gt;
&lt;p&gt;This gave me:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{% for tag in tags %} 
  {% if tag == &amp;quot;Modelling&amp;quot; %}| &amp;lt;a href=&amp;quot;/narrow-gauge-modelling&amp;quot;&amp;gt;Modelling&amp;lt;/a&amp;gt;{% endif %}
  {% if tag == &amp;quot;Loxley&amp;quot; %}| &amp;lt;a href=&amp;quot;/loxleybarton&amp;quot;&amp;gt;Loxley Barton Falls&amp;lt;/a&amp;gt;{% endif %}
{%- endfor %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which works perfectly. I am sure there is a more robust method but for now, for me, this is working great!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Sun, 17 Mar 2024 12:40:20 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2024-03-17-11ty-collection-tag-links/</guid>
    </item>
    <item>
      <title>Cleaning up the 11ty config</title>
      <link>https://www.simoncox.com/short-articles/2023-10-29-cleaning-up-the-11ty-config/</link>
      <description>&lt;p&gt;I read a couple of articles this week on cleaning up your 11ty config file, the initial one that I found from &lt;a href=&quot;https://moosedog.io/posts/clean-configuration-file-in-eleventy.html&quot;&gt;Robin Hoover&lt;/a&gt; and the second from &lt;a href=&quot;https://www.lenesaile.com/en/blog/organizing-the-eleventy-config-file/&quot;&gt;Lene Saile&lt;/a&gt; in which Lene had added a 3rd method, adding another config file as a plugin which I then understood what I could do. Yes takes me a bit of time sometimes.&lt;/p&gt;
&lt;p&gt;I created a new folder named _11ty in my source folder and in that added new .js files for each of the plugins, functions, filters and collections I needed, and in each added the code for them. This included the module.exports section but with slightly changed code.&lt;/p&gt;
&lt;p&gt;plugin.js code example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const CleanCSS = require(&amp;quot;clean-css&amp;quot;);

module.exports = eleventyConfig =&amp;gt; {
  eleventyConfig.addFilter(&amp;quot;cssmin&amp;quot;, function(code) {
    return new CleanCSS({}).minify(code).styles;
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The module.exports line has changed from:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = function(eleventyConfig) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = eleventyConfig =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all the plugin and filter code tucked into its own file for convenience and ease of maintenance my .eleventy.js file now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = function(eleventyConfig) {
  // Plugins
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/cssmini.js&amp;quot;)); // CSS minification
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/rss.js&amp;quot;)); // RSS feed plugin
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/datetime.js&amp;quot;)); // Date Time plugin
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/collections.js&amp;quot;)); // Collections
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/image.js&amp;quot;)); // Image plugin
  eleventyConfig.addPlugin(require(&amp;quot;./src/_11ty/passthroughs.js&amp;quot;)); // passthroughs

  return {
    dir: {
      input: &amp;quot;src&amp;quot;,
      output: &amp;quot;public&amp;quot;
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the paths to each plug in I have used.&lt;/p&gt;
&lt;p&gt;Yes, this is more of a note to myself for the next time I need to do this but I hope it helps someone else too!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Sun, 29 Oct 2023 16:11:20 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-10-29-cleaning-up-the-11ty-config/</guid>
    </item>
    <item>
      <title>11ty draft posts</title>
      <link>https://www.simoncox.com/short-articles/2023-09-01-11ty-draft-posts/</link>
      <description>&lt;h2 id=&quot;set-up-a-draft-system-in-11ty&quot; tabindex=&quot;-1&quot;&gt;Set up a draft system in 11ty&lt;/h2&gt;
&lt;p&gt;Now this and a few other sites I have are &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;running on 11ty&lt;/a&gt; I have been meaning to add a way of marking a post as a draft while I am writing it - just in case I need to publish a short while getting an article ready.&lt;/p&gt;
&lt;p&gt;There are several complex explanations of how to do this out there including one from &lt;a href=&quot;https://remysharp.com/2019/06/26/scheduled-and-draft-11ty-posts&quot;&gt;Remy Sharps from 2019&lt;/a&gt; and the &lt;a href=&quot;https://www.11ty.dev/docs/quicktips/draft-posts/&quot;&gt;Quick Tip in the 11ty docs&lt;/a&gt;. I am sure they are all great
but I wanted something simple and easy to use and I didn&#39;t need to schedule anything - well not yet anyway.&lt;/p&gt;
&lt;p&gt;I found this one on github from &lt;a href=&quot;https://github.com/pdehaan/11ty-drafts&quot;&gt;Peter Dehaan&lt;/a&gt; which explained how this works easily enough for me to comprehend and implement.&lt;/p&gt;
&lt;p&gt;With the added src.11tydata.js file all I need to is add a draft: true to the front matter and the post will not be included in the build, including lists of posts, my mxl sitemap and my rss feed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: 11ty draft posts
description: Setting a post to draft in 11ty while writing
topic: web
draft: true
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;update-2024%2F11%2F25&quot; tabindex=&quot;-1&quot;&gt;Update 2024/11/25&lt;/h2&gt;
&lt;p&gt;There has been un update in 11ty v3 with an &lt;a href=&quot;https://www.11ty.dev/docs/config-preprocessors/#example-drafts&quot;&gt;easier method&lt;/a&gt;&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Fri, 01 Sep 2023 17:32:12 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-09-01-11ty-draft-posts/</guid>
    </item>
    <item>
      <title>Solving a date display issue in 11ty templates</title>
      <link>https://www.simoncox.com/short-articles/2023-07-07-solving-a-date-display-issue-in-11ty-templates/</link>
      <description>&lt;p&gt;I have recently changed the &lt;a href=&quot;https://www.simoncox.com/post/2023-07-06-lastmod-in-xml-sitemap-for-11ty/&quot;&gt;lastmod date in my xml site map to be accurate&lt;/a&gt; now that Google are wanting that. Part of what Google wanted was for the page to state the update date on it. I pulled this data from the posts front matter but because I had set an 11ty filter for the sitemap the output on the page was the full UTC:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;strong&gt;2023-04-03T00:00:00.000&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The resolution was to add .toDateString() to the front matter item, dateUpdated, in the template giving me:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dateUpdated.toDateString()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Which displays as:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;strong&gt;Mon Apr 03 2023&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Far better!&lt;/em&gt;&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Fri, 07 Jul 2023 15:53:37 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-07-07-solving-a-date-display-issue-in-11ty-templates/</guid>
    </item>
    <item>
      <title>11ty image shortcode best practice</title>
      <link>https://www.simoncox.com/short-articles/2023-06-06-11ty-image-shortcode-best-practice/</link>
      <description>&lt;p&gt;Just deployed a site using the &lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;11ty image plugin&lt;/a&gt; and it is working well. I did have some issues with set up - wanted the hi-res images in a separate folder outside of src and public folders and acheived that by setting an  external source -  would be nice if this were in the base set up:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let imageSrc = `hi-res-images/${src}`;
let metadata = await Image(imageSrc, {    
widths: [180, 343, 647, 1200],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then the shortcode in the functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eleventyConfig.addAsyncShortcode(&amp;quot;image&amp;quot;, imageShortcode);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But then I was having issues where the image shown on full screen was the low res version - not good! Took me an age to work out the issue till I noticed some errant code in the source.&lt;/p&gt;
&lt;p&gt;In Markdown please leave a space before and after the image shortcode - else it gets mixed up and bits of the picture element get wrapped in a p tag and become not a picture element!&lt;/p&gt;
&lt;p&gt;This doesn&#39;t work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lorum ipsum dolar bla bla
% image &amp;quot;image.jpg&amp;quot;, &amp;quot;alt text&amp;quot; %
Lorum ipsum dolar bla bla
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this does:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lorum ipsum dolar bla bla

% image &amp;quot;image.jpg&amp;quot;, &amp;quot;alt text&amp;quot; %

Lorum ipsum dolar bla bla
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leave some space!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Tue, 06 Jun 2023 15:46:51 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-06-06-11ty-image-shortcode-best-practice/</guid>
    </item>
    <item>
      <title>11ty conditional canonical</title>
      <link>https://www.simoncox.com/short-articles/2023-02-13-11ty-conditional-canonical/</link>
      <description>&lt;p&gt;GSC was flagging some Canonical issue on this site so I decided to add the canonical using the page.url parameter in my base.njk nunjucks include.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;https://www.simoncox.com{{ page.url }}&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;However, I have already included a Canonical in the frontmatter of some of my posts and there may be reasons why i&#39;d want the canonical to be on a different url. So I added a conditional to state that if the frontmatter for canonical exists then use it else fall back to the standard page url version. And that gave me this:&lt;br /&gt;
&lt;code&gt;{% if canonical %}&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;{{ canonical }}&amp;quot;&amp;gt;{% else %}&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;https://www.simoncox.com{{ page.url }}&amp;quot;&amp;gt;{% endif %}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Simple but effective.
^ to &lt;a href=&quot;https://mrqwest.co.uk/blog/escaping-code-in-11ty/&quot;&gt;MrQuest for the tip showing me how to display raw njk code!&lt;/a&gt;&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Mon, 13 Feb 2023 14:17:55 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-02-13-11ty-conditional-canonical/</guid>
    </item>
    <item>
      <title>Remove 404 file from sitemap.xml in 11ty</title>
      <link>https://www.simoncox.com/short-articles/2023-02-06-remove-404-file-from-sitemap-xml-in-11ty/</link>
      <description>&lt;p&gt;I realised that the sitemap.xml file was listing the 404 page - we really don&#39;t want that in there as the search engines don&#39;t want it. &lt;a href=&quot;https://www.simoncox.com/post/2023-07-06-lastmod-in-xml-sitemap-for-11ty/&quot;&gt;The 11ty sitemap.njk file&lt;/a&gt; had the answer in its frontmatter for any page you don&#39;t want in a collection, and that includes the All collection, I added this in the frontmatter of the 404 page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eleventyExcludeFromCollections: true

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then any page I want to manually jeep out of the sitemap I can add &lt;em&gt;sitemapIgnore: true&lt;/em&gt; to the frontmatter.&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Mon, 06 Feb 2023 18:08:58 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-02-06-remove-404-file-from-sitemap-xml-in-11ty/</guid>
    </item>
    <item>
      <title>11ty starter video</title>
      <link>https://www.simoncox.com/short-articles/2023-02-03-11ty-starter-video/</link>
      <description>&lt;p&gt;There are many 11ty resources available but the one that I started with to get me going is
Kevin Powell&#39;s &lt;a href=&quot;https://www.youtube.com/watch?v=4wD00RT6d-g&quot;&gt;Turn static HTML/CSS into a blog with CMS using the JAMStack&lt;/a&gt; and I keep going back to it!&lt;/p&gt;
</description>
      
      <enclosure url="https://www.simoncox.com/assets/img/content/itsjustashort.webp" type="image" />
      <pubDate>Fri, 03 Feb 2023 24:00:00 GMT</pubDate>
      <dc:creator>Simon Cox</dc:creator>
      <guid>https://www.simoncox.com/short-articles/2023-02-03-11ty-starter-video/</guid>
    </item>
  </channel>
</rss>