<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Drupal planet - Pixelite]]></title><description><![CDATA[A technology blog or rant area depending on the topic]]></description><link>https://www.pixelite.co.nz/</link><image><url>https://www.pixelite.co.nz/favicon.png</url><title>Drupal planet - Pixelite</title><link>https://www.pixelite.co.nz/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Tue, 19 May 2026 11:35:04 GMT</lastBuildDate><atom:link href="https://www.pixelite.co.nz/tag/drupal-planet/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Bots, scrapers, and proxies: defending Drupal sites in an automated internet]]></title><description><![CDATA[Bots are over half of web traffic. Here is the layered defence I use to keep Drupal sites running - companion to my DrupalSouth Wellington 2026 talk.]]></description><link>https://www.pixelite.co.nz/article/bots-scrapers-and-proxies-defending-drupal-sites-in-an-automated-internet/</link><guid isPermaLink="false">6a091a003e6c5e00071c0341</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[security]]></category><category><![CDATA[Bot management]]></category><category><![CDATA[Cloudflare]]></category><category><![CDATA[WAF]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sun, 17 May 2026 02:32:56 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2026/05/feature-bots-scrapers-proxies.svg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2026/05/feature-bots-scrapers-proxies.svg" alt="Bots, scrapers, and proxies: defending Drupal sites in an automated internet"><p>Over half of all web traffic in 2024 was automated. That is the headline number from the Imperva 2025 Bad Bot Report, and it is the first time bots have outnumbered humans in more than a decade. Drupal sites sit squarely in that traffic mix, and the old defensive playbook &#x2014; block an IP, ban a user agent, drop a <code>robots.txt</code> entry, lean on Fail2ban &#x2014; does not hold up anymore.</p><p>This is the companion post to my DrupalSouth Wellington 2026 talk, <em>Bots, scrapers, and proxies: defending Drupal sites in an automated internet</em>. The talk walked through the defences I actually use at amazee.io and recommend on client sites. The post covers the same ground, with a bit more room to show config and link out to the projects.</p><h2 id="what-actually-changed">What actually changed</h2><p>The technical context underneath bot defence has shifted in three ways that matter:</p><ul><li><strong>Residential proxy networks</strong>. Scrapers no longer come from a handful of cloud subnets you can block. They route through real consumer IP addresses, often unwittingly donated by free-VPN users or piggy-backed off shady SDKs in mobile apps.</li><li><strong>Headless browsers everywhere</strong>. Playwright and Puppeteer have made it trivial to render JavaScript-heavy pages at scale. A page that needed a real human five years ago can be scraped today by anyone with a laptop.</li><li><strong>AI-driven scraping</strong>. Volume is up sharply because every new LLM needs training data, and there is now a steady drip of new crawlers showing up. Meta&apos;s <code>externalagent</code> is one recent example. There will be more.</li></ul><p>Mimicry is now the baseline, not the edge case. A modern scraper will rotate IPs, randomise user agents, replay realistic TLS fingerprints, and pace itself slowly enough to look like a real user. You cannot rely on signal that lives in one HTTP header.</p><h2 id="the-scale-of-it">The scale of it</h2><p>If this still sounds like a niche problem, the numbers say otherwise.</p><ul><li><strong>51%</strong> - share of web traffic that was automated in 2024, per the Imperva 2025 Bad Bot Report.</li><li><strong>+96%</strong> - year-on-year growth in some popular bot services across Pantheon&apos;s hosting fleet in their July 2025 data.</li><li><strong>1B+</strong> - unique monthly visitors Pantheon sees across its platform, which is the size of dataset those numbers are coming from.</li></ul><p>On the amazee.io platform globally, 13% of incoming requests can be flagged as non-human based on the user agent alone. That is the lazy bots. The actual share of automated traffic is higher once you account for the ones that try to blend in. In absolute terms it adds up to hundreds of millions of requests every month.</p><h2 id="the-goal-is-not-to-block-all-bots">The goal is not to block all bots</h2><p>Before going through the defences, one thing I am careful to say up front, both on stage and here: the goal is not to block all bots. That is unwinnable, and the closer you get to it the more real users you break.</p><p>Search crawlers, RSS readers, uptime monitors, link-preview generators in Slack and iMessage, accessibility tooling - all bots, all wanted. The goal is to reduce abuse where it hurts most, on the endpoints that cost you real money or real performance, while leaving everything else alone.</p><h2 id="drupal-native-defences">Drupal-native defences</h2><p>The defences closest to your application are the smartest. They can see the path, the user, the form, the cache state. They are also the most expensive per blocked request, because every block at this layer has already cost you a full PHP bootstrap.</p><h3 id="perimeter">Perimeter</h3><p>The <a href="https://www.drupal.org/project/perimeter">Perimeter module</a> drops requests matching known-bad patterns: <code>/wp-admin</code>, <code>/.env</code>, <code>xmlrpc.php</code>, all the WordPress scanner noise that hits every Drupal site daily. It is the cheapest win on the list. It will not stop a serious scraper, but it will keep your logs clean and your error rate honest.</p><h3 id="crowdsec-and-abuseipdb">CrowdSec and AbuseIPDB</h3><p><a href="https://www.crowdsec.net/">CrowdSec</a> is a local agent plus a community blocklist. Every site running CrowdSec contributes detected attacks back to a shared signal, and pulls down the latest list of bad actors. It is the closest thing the open-source world has to a distributed reputation system.</p><p><a href="https://www.abuseipdb.com/">AbuseIPDB</a> is a reputation lookup service. You query an IP, you get a confidence score. It is most useful on the forms and login flows where you can afford the latency of an external API call. Both are available as Drupal modules.</p><h3 id="facet-bot-blocker">Facet Bot Blocker</h3><p>If you run Search API with facets, this is the single cheapest huge win available to you. Faceted search URLs are catnip for scrapers: every combination of filters is a new URL, every URL is uncached, every uncached request hits the database. A bot that crawls a faceted listing can take a site down without trying.</p><p>The <a href="https://www.drupal.org/project/facet_bot_blocker">Facet Bot Blocker module</a> acts as a rate limit on requests that include at least one facet in the URL. Configure it to use Redis or memcache for the counter so you are not making the problem worse by hitting the database to record the request. On one of our hosting customers, this one module cut Search API load by more than half.</p><h3 id="form-side-defences">Form-side defences</h3><p>Logins, registrations, password resets and contact forms all need their own treatment, separate from page-level defence:</p><ul><li><a href="https://www.drupal.org/project/honeypot">Honeypot</a> - invisible field plus a time-based check. Cheap, fast, surprisingly effective against the dumb half of form spam.</li><li><a href="https://www.drupal.org/project/antibot">Antibot</a> - requires JavaScript to submit, blocks the bots that do not run JS.</li><li>CAPTCHA, reCAPTCHA, or <a href="https://www.cloudflare.com/products/turnstile/">Cloudflare Turnstile</a> - full challenge. Use the lightest option that works, and ideally only after Honeypot and Antibot have already rejected the easy cases.</li><li><a href="https://www.drupal.org/project/hidden_captcha">Hidden CAPTCHA</a> - bridges the gap when you want a CAPTCHA-style check without the accessibility cost of a visible challenge.</li></ul><h3 id="the-trade-off-the-project-pages-dont-mention">The trade-off the project pages don&apos;t mention</h3><p>Every block at the Drupal layer has already cost you a PHP bootstrap. That is fine when the absolute volume is small. It is not fine when you are eating hundreds of millions of bot requests and bootstrapping PHP for each one. This is why you cannot stop at the application layer.</p><h2 id="web-server-and-infrastructure">Web server and infrastructure</h2><p>One layer out, the web server can drop requests before PHP ever runs. The trade-off flips: you save the bootstrap cost, but you lose access to application context.</p><h3 id="rate-limiting-and-geo-blocking">Rate limiting and geo blocking</h3><p>nginx ships with <code>limit_req_zone</code>, Apache has <code>mod_ratelimit</code>. Both are blunt but effective on volume. A starting point for nginx looks roughly like this:</p><pre><code class="language-nginx">limit_req_zone $binary_remote_addr zone=search:10m rate=10r/m;

location /search {
    limit_req zone=search burst=5 nodelay;
    proxy_pass http://drupal;
}
</code></pre><p>Ten search requests per minute, per IP, with a burst of five. Tune to taste. The <code>$binary_remote_addr</code> key is cheap on memory; a 10MB zone holds around 160,000 IPs.</p><p>Geo blocking is the other infrastructure-level lever. It is pragmatic and occasionally controversial. If your audience is the New Zealand public sector, blocking inbound from regions you do not serve is a defensible call. If your audience is global, it is not. Know your traffic before reaching for it.</p><h3 id="modsecurity-and-the-owasp-crs">ModSecurity and the OWASP CRS</h3><p>ModSecurity with the <a href="https://owasp.org/www-project-modsecurity-core-rule-set/">OWASP Core Rule Set</a> is a proper WAF you can self-host. Once tuned, it is real protection. The tuning is the catch &#x2014; out of the box it will flag Drupal admin actions, file uploads, anything that looks like SQL in a body or a query string. Expect to spend real time pruning rules and adding exceptions for legitimate site behaviour before you stop generating false positives.</p><h3 id="cache-discipline">Cache discipline</h3><p>A request that hits the cache costs you nothing. Whatever else you do, get your cache headers right. Vary on the bits that need to vary, cache aggressively on the bits that do not, and lean on the page cache or the reverse proxy in front of Drupal. The cheapest bot is the one that asks for a page you have already served.</p><p>(shamless plug) you can also use my site Caching Score to review your current caching setup, to see if there is anything better you can be doing.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.cachingscore.com/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Caching Score</div><div class="kg-bookmark-description">Assess how strong the caching capabilities of any given site is.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.cachingscore.com/apple-touch-icon.png" alt="Bots, scrapers, and proxies: defending Drupal sites in an automated internet"><span class="kg-bookmark-author">Caching Score</span><span class="kg-bookmark-publisher">sean.hamlinamazee.io</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.cachingscore.com/icons/opengraph.jpg" alt="Bots, scrapers, and proxies: defending Drupal sites in an automated internet"></div></a></figure><h3 id="what-this-layer-is-bad-at">What this layer is bad at</h3><p>Rate limits, ModSecurity rules and geo blocks are great at volume and bad at quality. They cannot tell a scraper trickling one request per minute apart from a real user. For that you need either the edge or the application.</p><h2 id="edge-and-paid-bot-management">Edge and paid bot management</h2><p>The edge is where the big vendors live, and it is where you push the cheapest blocks. A scraper rejected by Cloudflare at the network edge never gets to your origin at all.</p><h3 id="cloudflare">Cloudflare</h3><p>The free tier already includes Bot Fight Mode, basic challenges, and Turnstile. For most small-to-medium Drupal sites, this is a good baseline at zero extra cost. The paid Bot Management product adds custom rule logic, JA3 and JA4 TLS fingerprinting, and machine-learning-based bot scoring you can wire into firewall rules. The jump from free to paid is significant in price; the jump in capability is also significant.</p><h3 id="fastly-akamai-and-the-rest">Fastly, Akamai, and the rest</h3><p>Fastly offers the Next-Gen WAF (originally Signal Sciences) with a Bot Management add-on. Akamai sits at the enterprise tier with the most sophisticated fingerprinting available, and a price tag to match. Beyond those, there is AWS WAF with Bot Control, DataDome, HUMAN, and Imperva &#x2014; all credible, all paid, all priced for sites where bot abuse is costing real money.</p><h3 id="the-trade-offs-nobody-puts-on-the-sales-deck">The trade-offs nobody puts on the sales deck</h3><p>Bot Management at the edge solves real problems. It also comes with real costs that the vendor demos skip past:</p><ul><li><strong>Cost</strong>. Bot Management is almost always an add-on to the core WAF subscription, and the pricing escalates fast with traffic.</li><li><strong>Vendor lock-in</strong>. Your rules, your dashboards, your observability all live in the vendor&apos;s UI. Migrating off is painful.</li><li><strong>Accessibility and SEO</strong>. Aggressive challenges break real users, and search bots that fail a challenge will hurt your rankings. Test both before turning anything up.</li><li><strong>Rules live outside your application codebase</strong>. They drift, they are not versioned alongside the code that depends on them, and a rule change can break a feature without any commit to point to.</li><li><strong>False positives are invisible to you</strong>. By default, blocked requests do not reach your logs. You will not know which real users were turned away unless you specifically ask for that signal.</li></ul><h2 id="anubis">Anubis</h2><p>The newest piece in the picture, and the one that has me genuinely interested.</p><h3 id="what-anubis-is">What Anubis is</h3><p><a href="https://anubis.techaro.lol/">Anubis</a> is an open-source reverse proxy (MIT licensed) that sits in front of your site and issues a proof-of-work challenge to clients before letting them through. It was built specifically for the AI scraper era &#x2014; for the case where the scraper is mimicking a real browser well enough that classifying it on signal alone has stopped working.</p><h3 id="why-proof-of-work-not-captcha">Why proof-of-work, not CAPTCHA</h3><p>The interesting move with Anubis is who pays the cost. A real user pays a few hundred milliseconds of CPU once when they first arrive, and never sees it again for the lifetime of the cookie. A scraper hitting you a million times pays the cost a million times.</p><p>That asymmetry is the whole point. CAPTCHAs put the cost on humans (the people who lose patience trying to identify traffic lights). Anubis puts it on whoever is doing the hammering. That is closer to the right shape of the trade.</p><h3 id="where-to-put-it">Where to put it</h3><p>You do not want Anubis in front of your whole site. You want it in front of the endpoints that are expensive and uncacheable. From the talk, my shortlist:</p><ul><li>Search endpoints</li><li>Facet and filter URLs</li><li>Pagination tails - <code>?page=2348</code> is not a real user</li><li>Login, register, password reset</li><li>Spicy forms (contact, anything that triggers an email)</li><li>Authenticated user flows</li><li>Anything expensive and uncacheable</li></ul><p>Static pages stay fast. The cache stays warm. The PoW cost only applies on the routes where it earns its keep.</p><h3 id="but-what-about-googlebot">But what about Googlebot?</h3><p>This is the first question every site owner asks, and the answer is good. Anubis ships with allowlists for known good crawlers, matching IP ranges against the published lists from Google, Bing, and the rest. The allowlist is maintained upstream, which means you need to keep Anubis deployed on a reasonable cadence to pull in the latest changes. New legitimate crawlers do show up.</p><h3 id="demo-site">Demo site</h3><p>You can see Anubis in action with a demo Drupal 11 site I put together, the login form has Anubis in front of it, the homepage does not.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://spicy.seanhamlin.website/user/login"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Log in | Drush Site-Install</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://spicy.seanhamlin.website/core/themes/olivero/favicon.ico" alt="Bots, scrapers, and proxies: defending Drupal sites in an automated internet"><span class="kg-bookmark-author">Drush Site-Install</span></div></div></a></figure><h2 id="putting-the-layers-together">Putting the layers together</h2><p>None of these defences is a silver bullet on its own. Each layer is cheap at one thing and bad at another, and the trick is matching the layer to the threat.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2026/05/bot-defence-layers-2.svg" class="kg-image" alt="Bots, scrapers, and proxies: defending Drupal sites in an automated internet" loading="lazy" width="283" height="150"><figcaption>Layered defence diagram showing requests flowing from clients through Edge/CDN, Anubis, web server, and Drupal, with cost-to-block increasing as you move closer to the application.</figcaption></figure><p>Block the cheap traffic at the edge. Block the lazy bots with rate limits and ModSecurity at the web server. Put Anubis in front of the endpoints that are expensive and uncacheable. Let Drupal-native modules handle the application-aware decisions where you actually need to see the user, the form, or the facet state.</p><h2 id="five-things-to-take-away">Five things to take away</h2><ol><li><strong>No single layer is enough.</strong> Stack them. The edge handles raw volume, the web server handles patterns, and the application is the only thing that can see real user and form context.</li><li><strong>Match the protection to the threat.</strong> A login form needs different defence to a faceted search results page.</li><li><strong>Measure before you defend.</strong> Look at your actual traffic. Find your most-hit uncacheable endpoints. Defend those first.</li><li><strong>Watch accessibility and SEO.</strong> Every challenge you add is a tax on a real user or a real crawler. The cost of false positives is invisible unless you go looking.</li><li><strong>Plan for adversarial improvement.</strong> Whatever you deploy today, the scrapers get a turn next. Pick defences you can iterate on.</li></ol><h2 id="one-last-thing">One last thing</h2><p>You do not need to win the bot war. You just need to make your site a worse target than the next one.</p><hr><p>The slides from the talk are <a href="https://drupalsouth.org/events/drupalsouth-wellington-2026/schedule/4341">on the DrupalSouth schedule page</a>. The recording will be posted here once the DrupalSouth team have edited and uploaded it &#x2014; check back in a few weeks.</p>]]></content:encoded></item><item><title><![CDATA[Drupal and the Open Web in the Australian Government - 2024 edition]]></title><description><![CDATA[Have you ever wondered how popular Drupal is in your local state and at the Australian Federal Government level? 2024 edition.]]></description><link>https://www.pixelite.co.nz/article/drupal-and-the-open-web-in-the-australian-government-2024-edition/</link><guid isPermaLink="false">65ff96f7004d620006ac3ff4</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Statistics]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sat, 30 Mar 2024 07:09:13 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2024/03/Drupal-and-the-Open-Web-in-the-Australian-Government---DrupalSouth-2024---NEW-FORMAT.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2024/03/Drupal-and-the-Open-Web-in-the-Australian-Government---DrupalSouth-2024---NEW-FORMAT.png" alt="Drupal and the Open Web in the Australian Government - 2024 edition"><p><em>This is the complementary blog post for my <a href="https://drupalsouth.org/events/drupalsouth-sydney-2024/schedule/3041">DrupalSouth Sydney 2024 session</a>, and also v2.0 follow up of sorts from <a href="https://www.pixelite.co.nz/article/drupal-and-the-open-web-in-the-australian-government-2022/">the original 2022 version</a>. The full presentation was much longer than this blog post, this blog post is just going to highlight the core statistics and findings.</em></p><hr><p>Have you ever wondered how popular Drupal is in your local state and at the Australian Federal Government level? This blog post will help to answer that question, using open source tooling. The hope is that you gain some insight to the relative popularity of Drupal and appreciate more the impact you and Drupal have in Australia.</p><p>As this blog post is a follow up, you can also now start to see trends (data is around 13 months newer than the last time I did this).</p><h2 id="just-show-me-the-graphs">Just show me the graphs</h2><p>Disclaimer:</p><ul><li>This is based on Oct 20, 2023 data</li><li>The scoring is based off PageRank data, so the percentages are not raw counts of websites, but an approximation of how important the respective sites are compared to others (assumes a logarithmic base of 5).</li><li>Wappalyzer detection is not perfect (see the end of this blog post for upstreamed PRs), and there is still a fairly large portion of sites where the CMS cannot be identified</li><li>MoGs make this tricky (PageRank relies on incoming links, which break due to MoGs)</li><li>Only source <code>*.gov.au</code> domains considered (some Government sites use other TLDs)</li><li>Unlikely newly created websites are in the top 10 million just yet (due to how the algorithm of PageRank works)</li></ul><h3 id="all-sites-govau">All sites (*.gov.au)</h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/all1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1003" height="620"><figcaption>All sites (*.gov.au)</figcaption></figure><h3 id="federal-sites-not-state-based-domains">Federal sites (not state based domains)</h3><p>Programmes like <a href="https://www.govcms.gov.au/">GovCMS</a> are having an impact here. Also interesting that if you are not using Drupal, the chances are you have written something entirely custom.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/fed1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1003" height="620"><figcaption>Federal sites (every non-state based domain)</figcaption></figure><h3 id="victoria-vicgovau">Victoria <code>*.vic.gov.au</code></h3><p>The <a href="https://www.vic.gov.au/single-digital-presence">Single Digital Presence (SDP)</a> programme makes a mark in Victoria.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/vic1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="996" height="616"><figcaption>Victoria (*.vic.gov.au)</figcaption></figure><h3 id="new-south-wales-nswgovau">New South Wales <code>*.nsw.gov.au</code></h3><p>Large Drupal sites like <a href="https://www.nsw.gov.au/">https://www.nsw.gov.au/</a> and <a href="https://www.service.nsw.gov.au/">https://www.service.nsw.gov.au/</a> help to make Drupal dominant in NSW.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/nsw1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="996" height="616"><figcaption>New South Wales (*.nsw.gov.au)</figcaption></figure><h3 id="south-australia-sagovau">South Australia <code>*.sa.gov.au</code></h3><p>Squiz Matrix increasing in market share &#x2191;5.4% over 2022. There is a clear state led mandate here.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/sa1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>South Australia (*.sa.gov.au)</figcaption></figure><h3 id="western-australia-wagovau">Western Australia <code>*.wa.gov.au</code></h3><p>A lot of sites this time around are now identified (decrease of &#x2193;30.8% of unknown sites). Drupal also increased market share by &#x2191;9.9%. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/wa1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>Western Australia (*.wa.gov.au)</figcaption></figure><h3 id="tasmania-tasgovau">Tasmania <code>*.tas.gov.au</code></h3><p>The lowest usage of Drupal for any Australian state or territory and the highest percentage of Wordpress.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/tas1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>Tasmania (*.tas.gov.au)</figcaption></figure><h3 id="queensland-qldgovau">Queensland <code>*.qld.gov.au</code></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/qld1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>Queensland (*.qld.gov.au)</figcaption></figure><h3 id="australian-capital-territory-actgovau">Australian Capital Territory <code>*.act.gov.au</code></h3><p>The highest percentage of Squiz compared to any other Australia state or territory.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/act1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>Australian Capital Territory (*.act.gov.au)</figcaption></figure><h3 id="northern-territory-ntgovau">Northern Territory <code>*.nt.gov.au</code></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/nt1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="1000" height="618"><figcaption>Northern Territory (*.nt.gov.au)</figcaption></figure><h3 id="open-source-software-oss-cms-vs-proprietary-cms">Open Source Software (OSS) CMS vs Proprietary CMS</h3><p>For the CMS&apos; that can be identified, splitting them into 2 categories, <a href="https://en.wikipedia.org/wiki/Open-source_software">OSS</a> and <a href="https://en.wikipedia.org/wiki/Proprietary_software">Proprietary</a>. OSS is determined on whether the source code is freely available, and there is a licence that allows me to run it without paying someone.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/oss1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="986" height="609"><figcaption>Open Source Software (OSS) CMS vs Proprietary CMS</figcaption></figure><h3 id="drupal-sites-by-major-version">Drupal sites by major version</h3><p>For sites reporting as Drupal, Drupal 10 is the most popular. Still 5.4% of Drupal sites running Drupal 7 to which will be End-of-Life (EOL) in <a href="https://www.drupal.org/about/drupal-7/d7eol/partners">early 2025</a>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/d1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="986" height="609"><figcaption>Drupal by major version</figcaption></figure><h2 id="score-by-state-and-territory">Score by state and territory</h2><p>This is weighted by total score, broken down by federal/state/territory.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/state1.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="889" height="550"><figcaption>Scores by federal/state/territory in Australia</figcaption></figure><h2 id="observations-and-other-unusual-findings">Observations and other unusual findings</h2><h3 id="drupal-usage">Drupal usage</h3><blockquote class="kg-blockquote-alt">&#x201C;Drupal powers <strong>~29.9%</strong> of all digital experiences that you use in the Australian government. This is<strong>&#x2191;2.7%</strong> compared to 2022&#x201D;</blockquote><h3 id="drupal-growth">Drupal Growth</h3><blockquote class="kg-blockquote-alt">&#x201C;Relative to the growth of Australian government sites, Drupal adoption is growing faster&#x201D;</blockquote><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2024/03/SCR-20240330-lrpy.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2024 edition" loading="lazy" width="2000" height="267" srcset="https://www.pixelite.co.nz/content/images/size/w600/2024/03/SCR-20240330-lrpy.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2024/03/SCR-20240330-lrpy.png 1000w, https://www.pixelite.co.nz/content/images/size/w1600/2024/03/SCR-20240330-lrpy.png 1600w, https://www.pixelite.co.nz/content/images/2024/03/SCR-20240330-lrpy.png 2050w" sizes="(min-width: 1200px) 1200px"><figcaption>Drupal adoption is rising faster that Australian government sites are rising</figcaption></figure><h3 id="top-contender">Top contender</h3><blockquote class="kg-blockquote-alt">&#x201C;Squiz Matrix is the top contender with <strong>15.6%</strong>, and has a clear state lead mandate in 5 states/territories. This is<strong>&#x2191;3.5%</strong> compared to 2022&#x201D;</blockquote><h3 id="drupal-7-usage">Drupal 7 usage</h3><blockquote class="kg-blockquote-alt">&#x201C;Drupal 7 usage dropped from 15.8% in 2022 to 5.4% in 2024. This is&#x2193;65.8% compared to 2022. Most popular Drupal 7 site is https://www.sl.nsw.gov.au/&#x201D;</blockquote><p>Also after my presentation I got to meet the team behind the State Library of NSW, and they advised that they are due to upgrade to Drupal 10 anytime soon.</p><h3 id="tls-coverage-is-still-not-100">TLS coverage is still not 100%</h3><p>83 sites with HTTP only (a drop of &#x2193;46 since 2022)</p><!--kg-card-begin: html--><table style="border:none;border-collapse:collapse"><colgroup><col width="374px"><col width="183px"><col width="162px"><col width="120px"></colgroup><tbody><tr style="height:46px"><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Domain</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">CMS</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Page Rank</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Score</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="http://www.bom.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www.bom.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">5.63</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">8614</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="http://ips.gov.au/mailman/listinfo" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://ips.gov.au/mailman/listinfo</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.68</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1867</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="http://www.nntt.gov.au/Pages/Home-Page.aspx" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www.nntt.gov.au/Pages/Home-Page.aspx</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">microsoft-sharepoint</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.68</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1867</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="http://ajrp.awm.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://ajrp.awm.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">hcl-notes</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.6</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1642</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="http://services.land.vic.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://services.land.vic.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.56</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1539</span></p></td></tr></tbody></table><!--kg-card-end: html--><h3 id="if-in-doubt-add-a-number">If in doubt, add a number</h3><p>14 sites with <code>ww[0-9]</code> in the domain name (a drop of &#x2193;5 since 2022)</p><!--kg-card-begin: html--><table style="border:none;border-collapse:collapse"><colgroup><col width="374px"><col width="183px"><col width="162px"><col width="120px"></colgroup><tbody><tr style="height:46px"><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Domain</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">CMS</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Page Rank</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Poppins,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Score</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="https://www2.gbrmpa.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www2.gbrmpa.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">drupal</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">5.3</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">5065</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="https://www1.health.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www1.health.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.99</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">3075</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="https://www9.health.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www9.health.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.59</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1615</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="https://www1.aiatsis.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www1.aiatsis.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.53</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1467</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><a href="https://www2.sl.nsw.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:underline;-webkit-text-decoration-skip:none;text-decoration-skip-ink:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www2.sl.nsw.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.51</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Poppins,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1420</span></p></td></tr></tbody></table><!--kg-card-end: html--><p>I think this is often used like a form of poor mans version control, often archiving the previous version of the site. For some reason it is archived publicly.</p><h3 id="i-want-the-raw-data">I want the raw data!</h3><p>If you want to make your own visualisations of the data, or even just do random queries &#x201C;how popular is Spark CMS in Western Australia&#x201D;, you can download a CSV from <a href="https://bit.ly/dsau2024csv">https://bit.ly/dsau2024csv</a>. Slides are <a href="https://bit.ly/dsau2024">https://bit.ly/dsau2024</a>.</p><h3 id="upstreamed-enhancements">Upstreamed enhancements</h3><p>I found a lot of software running in certain states, so I upstreamed some changes to better detect these software packages:</p><ul><li>Better SilverStripe detection <a href="https://github.com/enthec/webappanalyzer/pull/120">#120</a></li><li>Datascape detection <a href="https://github.com/enthec/webappanalyzer/pull/122">#122</a></li><li>Spark CMS detection <a href="https://github.com/enthec/webappanalyzer/pull/123">#123</a></li><li>Jadu detection <a href="https://github.com/enthec/webappanalyzer/pull/126">#126</a></li><li>Engagement HQ detection <a href="https://github.com/enthec/webappanalyzer/pull/124">#124</a></li><li>Social Pinpoint detection <a href="https://github.com/enthec/webappanalyzer/pull/125">#125</a></li><li>Citizen Space detection <a href="https://github.com/enthec/webappanalyzer/pull/121">#121</a></li></ul><h2 id="comments">Comments</h2><p>I am keen to hear feedback on this data, and what can be done to improve the scoring. Also, if you can help fill in some of the &apos;unknown&apos; data, let me know, I am happy to craft another PR into WebAppAnalyzer.</p>]]></content:encoded></item><item><title><![CDATA[Drupal and the Open Web in the Australian Government - 2022 edition]]></title><description><![CDATA[Have you ever wondered how popular Drupal is in your local state and at the Australian Federal Government level?]]></description><link>https://www.pixelite.co.nz/article/drupal-and-the-open-web-in-the-australian-government-2022/</link><guid isPermaLink="false">6351dd8fb228110007e518d0</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Statistics]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sun, 13 Nov 2022 22:54:55 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1451187580459-43490279c0fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE3fHxwbGFuZXR8ZW58MHx8fHwxNjY4NDE3MjI1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1451187580459-43490279c0fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE3fHxwbGFuZXR8ZW58MHx8fHwxNjY4NDE3MjI1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Drupal and the Open Web in the Australian Government - 2022 edition"><p><em>This is the complementary blog post for my <a href="https://drupalsouth.org/events/drupalsouth-brisbane-2022/schedule/1662">DrupalSouth Brisbane 2022 session</a>.</em></p><p>Have you ever wondered how popular Drupal is in your local state and at the Australian Federal Government level? This blog post will help to answer that question, using open source tooling. The hope is that you gain some insight to the relative popularity of Drupal and appreciate more the impact you and Drupal have in Australia.</p><h2 id="existing-solutions">Existing solutions</h2><p>There are a number of websites that will claim to be able to give you this information. However they all will likely want you at some point to pay them money.</p><ul><li>wappalyzer.com</li><li>semrush.com</li><li>builtwith.com</li><li>whatcms.com</li><li>similartech.com</li><li>larger.io</li></ul><blockquote class="kg-blockquote-alt">&#x201C;I wanted an open way to do this&#x201D;</blockquote><p>As it turns out, you can plug a few things together to scrape the technologies in use</p><ul><li><a href="https://github.com/wappalyzer/wappalyzer ">Wappalyzer library</a> (open source)</li><li><a href="https://github.com/puppeteer/puppeteer">Puppeteer</a> (open source)</li></ul><h2 id="problem-1-how-to-get-a-list-of-all-australia-government-domains">Problem #1: How to get a list of all Australia Government domains</h2><p>If anything, there are too many sources of this information:</p><ul><li><a href="https://github.com/tb0hdan/domains">https://github.com/tb0hdan/domains</a> </li><li><a href="https://crt.sh/?q=gov.au">Let&apos;s Encrypt Certificate Transparency (CT) logs</a></li><li><a href="https://securitytrails.com/list/apex_domain/gov.au">SecurityTrails</a> </li></ul><p>A crawling method could also be done, loads of suitable seed sites, e.g.:</p><ul><li><a href="https://www.australia.gov.au/">https://www.australia.gov.au/</a></li><li><a href="https://www.directory.gov.au/departments-and-agencies">https://www.directory.gov.au/departments-and-agencies</a> </li></ul><p>The main issue is that just having a list of sites, does not convey the <em>importance</em> of the site relative to another site.</p><p>Enter <a href="https://www.domcop.com/top-10-million-websites">DomCop</a> to which publishes a list of the top 10 million domains on the internet, including a rank and Open PageRank. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/top10million.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1600" height="823" srcset="https://www.pixelite.co.nz/content/images/size/w600/2022/11/top10million.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2022/11/top10million.png 1000w, https://www.pixelite.co.nz/content/images/2022/11/top10million.png 1600w" sizes="(min-width: 1200px) 1200px"><figcaption>DomCop&apos;s top 10 million websites with a filter of <code>.gov.au</code> applied.</figcaption></figure><p>On top of suppling 5,795 Australian Government domains, there also is an &quot;Open Page Rank&quot; field. The PageRanks are calculated based on the Open data provided by <a href="http://commoncrawl.org/">Common Crawl</a> and <a href="https://about.commonsearch.org/">Common Search</a>.</p><h2 id="problem-2-what-is-pagerank">Problem #2: What is PageRank?</h2><p><a href="https://en.wikipedia.org/wiki/PageRank">PageRank</a> is a system for ranking web pages that Google&apos;s founders developed in 1996. A PageRank score of 0 is typically a low-quality website, whereas, a score of 10 would represent only the most authoritative sites on the web. It is logarithmic (with a base of 5).</p><p>A site with PageRank 3 is 5 times more authoritative than a site with PageRank 2.</p><h2 id="problem-3-machinery-of-government-mog-and-link-rot">Problem #3: Machinery of Government (MoG) and link rot</h2><p>Australian Governments sites are never static, they are constantly evolving. Sometimes several sites merge into 1, or sometimes 1 site splits into move sites.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/vhs.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1600" height="963" srcset="https://www.pixelite.co.nz/content/images/size/w600/2022/11/vhs.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2022/11/vhs.png 1000w, https://www.pixelite.co.nz/content/images/2022/11/vhs.png 1600w" sizes="(min-width: 720px) 720px"><figcaption>DHS Victoria is now closed. 3 sites now replace this 1 site.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/dese.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1600" height="655" srcset="https://www.pixelite.co.nz/content/images/size/w600/2022/11/dese.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2022/11/dese.png 1000w, https://www.pixelite.co.nz/content/images/2022/11/dese.png 1600w" sizes="(min-width: 720px) 720px"><figcaption>DESE is also now closed. 2 sites replace this 1 site.</figcaption></figure><h2 id="just-show-me-the-graphs">Just show me the graphs</h2><p>Disclaimer:</p><ul><li>This is based on Sept 22, 2022 data</li><li>The scoring is based off PageRank data, so the percentages are not raw counts of websites, but an approximation of how important the respective sites are compared to others (assumes a logarithmic base of 5).</li><li>Wappalyzer detection is not perfect (see the end of this blog post for upstreamed PRs), and there is still a fairly large portion of sites where the CMS cannot be identified</li><li>MoGs make this tricky (PageRank relies on incoming links, which break due to MoGs)</li><li>Only <code>*.gov.au</code> domains considered (some Government sites use other TLDs)</li><li>Unlikely newly created websites are in the top 10 million just yet (due to how PageRank works)</li></ul><h3 id="all-sites-govau">All sites (*.gov.au)</h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/all.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1003" height="620"><figcaption>All sites (*.gov.au)</figcaption></figure><h3 id="federal-sites-not-state-based-domains">Federal sites (not state based domains)</h3><p>Programmes like <a href="https://www.govcms.gov.au/">GovCMS</a> are having an impact here.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/fed.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1003" height="620"><figcaption>Federal sites (every non-state based domain)</figcaption></figure><h3 id="victoria-vicgovau">Victoria <code>*.vic.gov.au</code></h3><p>The <a href="https://www.vic.gov.au/single-digital-presence">Single Digital Presence (SDP)</a> programme makes a mark in Victoria.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/vic.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="996" height="616"><figcaption>Victoria (*.vic.gov.au)</figcaption></figure><h3 id="new-south-wales-nswgovau">New South Wales <code>*.nsw.gov.au</code></h3><p>Large Drupal sites like <a href="https://www.nsw.gov.au/">https://www.nsw.gov.au/</a> and <a href="https://www.service.nsw.gov.au/">https://www.service.nsw.gov.au/</a> help to make Drupal dominant in NSW.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/nsw.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="996" height="616"><figcaption>New South Wales (*.nsw.gov.au)</figcaption></figure><h3 id="south-australia-sagovau">South Australia <code>*.sa.gov.au</code></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/sa.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>South Australia (*.sa.gov.au)</figcaption></figure><h3 id="western-australia-wagovau">Western Australia <code>*.wa.gov.au</code></h3><p>A lot of unknown CMSs in WA, <s>including sites like <a href="https://ww2.health.wa.gov.au/">https://ww2.health.wa.gov.au/</a> which I still have no idea what the CMS used is</s>. <strong>Edit </strong>- a keen eyed developer has told me that <a href="https://ww2.health.wa.gov.au/~/media/System/Simulator%20Backgrounds/blackberry.ashx">because this URL exists</a>, so the WA Health site is generated by SiteCore. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/wa.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>Western Australia (*.wa.gov.au)</figcaption></figure><h3 id="tasmania-tasgovau">Tasmania <code>*.tas.gov.au</code></h3><p>The lowest usage of Drupal for any Australian state or territory and the highest percentage of Wordpress.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/tas.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>Tasmania (*.tas.gov.au)</figcaption></figure><h3 id="queensland-qldgovau">Queensland <code>*.qld.gov.au</code></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/qld.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>Queensland (*.qld.gov.au)</figcaption></figure><h3 id="australian-capital-territory-actgovau">Australian Capital Territory <code>*.act.gov.au</code></h3><p>The highest percentage of Squiz compared to any other Australia state or territory.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/act.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>Australian Capital Territory (*.act.gov.au)</figcaption></figure><h3 id="northern-territory-ntgovau">Northern Territory <code>*.nt.gov.au</code></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/nt.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1000" height="618"><figcaption>Northern Territory (*.nt.gov.au)</figcaption></figure><h3 id="open-source-software-oss-cms-vs-proprietary-cms">Open Source Software (OSS) CMS vs Proprietary CMS</h3><p>For the CMS&apos; that can be identified, splitting them into 2 categories, <a href="https://en.wikipedia.org/wiki/Open-source_software">OSS</a> and <a href="https://en.wikipedia.org/wiki/Proprietary_software">Proprietary</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/oss.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="986" height="609"><figcaption>Open Source Software (OSS) CMS vs Proprietary CMS</figcaption></figure><h3 id="drupal-sites-by-major-version">Drupal sites by major version</h3><p>For sites reporting as Drupal, Drupal 9 and 7 are the most popular.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/11/drupal.svg" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="986" height="609"><figcaption>Drupal by major version</figcaption></figure><p></p><h2 id="observations-and-other-unusual-findings">Observations and other unusual findings</h2><h3 id="1drupal-usage">#1 - Drupal usage</h3><blockquote class="kg-blockquote-alt">&#x201C;Drupal powers roughly <strong>27%</strong> of all digital experiences that you use in the Australian government&#x201D;</blockquote><h3 id="2top-contender">#2 - Top contender</h3><blockquote class="kg-blockquote-alt">&#x201C;Squiz Matrix is the top contender with <strong>15%</strong>, and has a clear state led mandate in certain states/territories&#x201D;</blockquote><h3 id="3tls-coverage">#3 - TLS coverage</h3><p>TLS coverage is not 100% - 129 domains found with no TLS</p><!--kg-card-begin: html--><table style="border:none;border-collapse:collapse"><colgroup><col width="428px"><col width="129px"><col width="162px"><col width="120px"></colgroup><tbody><tr style="height:46px"><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Domain</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">CMS</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Page Rank</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Score</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://www.bom.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www.bom.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">5.51</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">7,101</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://handle.slv.vic.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://handle.slv.vic.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.47</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,332</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://www.mbsonline.gov.au/internet/mbsonline/publishing.nsf/Content/Home" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www.mbsonline.gov.au/internet/mbsonline/publishing.nsf/Content/Home</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">hcl-notes</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.45</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,289</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://onesearch.slq.qld.gov.au/primo-explore/search?vid=SLQ" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://onesearch.slq.qld.gov.au/primo-explore/search?vid=SLQ</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.41</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,209</span></p></td></tr><tr style="height:30px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 13.5pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://www.majorprojects.planning.nsw.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www.majorprojects.planning.nsw.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 12.7297pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.41</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 13.8072pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,209</span></p></td></tr></tbody></table><!--kg-card-end: html--><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/10/bom.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="2000" height="947" srcset="https://www.pixelite.co.nz/content/images/size/w600/2022/10/bom.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2022/10/bom.png 1000w, https://www.pixelite.co.nz/content/images/size/w1600/2022/10/bom.png 1600w, https://www.pixelite.co.nz/content/images/2022/10/bom.png 2128w" sizes="(min-width: 1200px) 1200px"><figcaption>The second most trafficked site in the Australian Government does not support TLS. Instead this awkward redirect page is used. And a sad face emoji. Sad face indeed.</figcaption></figure><h3 id="4if-in-doubt-add-a-number">#4 - If in doubt, add a number</h3><p>19 domains found with <code>ww[number]</code> as a subdomain.</p><!--kg-card-begin: html--><table style="border:none;border-collapse:collapse"><colgroup><col width="419px"><col width="154px"><col width="154px"><col width="154px"></colgroup><tbody><tr style="height:41px"><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Domain</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">CMS</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:13px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Page Rank</span></p></td><td style="background-color:#0F0926;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:19px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:16pt;font-family:Ubuntu,sans-serif;color:#F2FAFD;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Score</span></p></td></tr><tr style="height:34px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><a href="https://www2.gbrmpa.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www2.gbrmpa.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">drupal</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 7.2692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.85</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 11.7692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">2,455</span></p></td></tr><tr style="height:34px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><a href="https://www1.health.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www1.health.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 7.2692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.62</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 11.7692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,695</span></p></td></tr><tr style="height:34px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><a href="https://ww2.health.wa.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://ww2.health.wa.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 7.2692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.49</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 11.7692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,375</span></p></td></tr><tr style="height:34px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><a href="http://www9.health.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">http://www9.health.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">unknown</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 7.2692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.3</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 11.7692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1,013</span></p></td></tr><tr style="height:34px"><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-left: 18pt;margin-top:0pt;margin-bottom:0pt;"><a href="https://www0.landgate.wa.gov.au/" style="text-decoration:none;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">https://www0.landgate.wa.gov.au/</span></a></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">squiz-matrix</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 7.2692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">4.25</span></p></td><td style="background-color:#FFFFFF;opacity:1;filter:alpha(opacity=100);vertical-align:bottom;border-left:solid #CCCCCC 1px;border-right:solid #CCCCCC 1px;border-top:solid #CCCCCC 1px;border-bottom:solid #CCCCCC 1px;padding-top:2px;padding-right:3px;padding-bottom:2px;padding-left:3px"><p dir="ltr" style="line-height:1.38;margin-right: 11.7692pt;text-align: right;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:10pt;font-family:Ubuntu,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">935</span></p></td></tr></tbody></table><!--kg-card-end: html--><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2022/10/doh.png" class="kg-image" alt="Drupal and the Open Web in the Australian Government - 2022 edition" loading="lazy" width="1140" height="366" srcset="https://www.pixelite.co.nz/content/images/size/w600/2022/10/doh.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2022/10/doh.png 1000w, https://www.pixelite.co.nz/content/images/2022/10/doh.png 1140w"><figcaption>When you run out of subdomains, just add a number.</figcaption></figure><h3 id="5you-cannot-kill-dreamweaver">#5 - You cannot kill Dreamweaver</h3><p>15 sites found in 2022. </p><h2 id="extending-this-for-the-future">Extending this for the future</h2><ul><li>Crawl other domain spaces, e.g. the New Zealand government domain space <code>*.govt.nz</code></li><li>Make a website and publish this data quarterly (DomCop&apos;s data updates around this frequency)</li><li>Measure trends over time</li></ul><h2 id="upstreamed-enhancements">Upstreamed enhancements</h2><p>These are all to make the detection of CMS&apos; and Javascript frameworks more accurate for Australia Government sites.</p><ul><li>Ripple <a href="https://github.com/wappalyzer/wappalyzer/pull/6827">#6827</a> </li><li>Dreamweaver <a href="https://github.com/wappalyzer/wappalyzer/pull/6828">#6828</a> </li><li>HCL DX <a href="https://github.com/wappalyzer/wappalyzer/pull/6829">#6829</a> </li><li>MODX <a href="https://github.com/wappalyzer/wappalyzer/pull/6832">#6832</a> </li><li>Squiz Matrix <a href="https://github.com/wappalyzer/wappalyzer/pull/6833">#6833</a></li><li>Optimizely CMS <a href="https://github.com/wappalyzer/wappalyzer/pull/6834">#6834</a></li><li>Umbraco <a href="https://github.com/wappalyzer/wappalyzer/pull/6835">#6835</a></li><li>Elcom <a href="https://github.com/wappalyzer/wappalyzer/pull/6836">#6836</a> </li><li>Lagoon <a href="https://github.com/wappalyzer/wappalyzer/pull/6861">#6861</a></li><li>OSS flags and links <a href="https://github.com/wappalyzer/wappalyzer/pull/6902">#6902</a></li><li>Squiz Matrix #2 <a href="https://github.com/wappalyzer/wappalyzer/pull/6925">#6925</a></li><li>Ektron CMS <a href="https://github.com/wappalyzer/wappalyzer/pull/6924">#6924</a></li><li>SiteCore <a href="https://github.com/wappalyzer/wappalyzer/pull/6926">#6926</a></li></ul><h2 id="raw-data">Raw data</h2><p>If you want to do your own analysis, here is a link to a <a href="https://gist.github.com/seanhamlin/077002e9775f3c1bc1027f4ba4d380bd">full CSV</a> dump.</p><h2 id="comments">Comments</h2><p>I am keen to hear feedback on this data, and what can be done to improve the scoring. Also, if you can help fill in some of the &apos;unknown&apos; data, let me know, I am happy to craft another PR into Wappalyzer.</p>]]></content:encoded></item><item><title><![CDATA[Fastly Soft Purge vs Instant Purge - what is the difference?]]></title><description><![CDATA[What Fastly Soft Purge is, when it is useful, and how to use it.]]></description><link>https://www.pixelite.co.nz/article/fastly-soft-purge-vs-instant-purge/</link><guid isPermaLink="false">61c23fb3aa1adc0007651217</guid><category><![CDATA[Fastly]]></category><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Caching]]></category><category><![CDATA[Varnish]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Wed, 29 Dec 2021 04:36:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1503164046765-c2dd32d51708?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxmZWF0aGVyfGVufDB8fHx8MTY0MDc1MjQyNQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1503164046765-c2dd32d51708?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxmZWF0aGVyfGVufDB8fHx8MTY0MDc1MjQyNQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Fastly Soft Purge vs Instant Purge - what is the difference?"><p>This topic came up a while ago when Acquia was looking to <a href="https://www.drupal.org/project/acquia_purge/issues/3254161">introduce Soft Purge as an option for their Acquia purge module</a> in Drupal (N.B. not to be confused by the <a href="https://www.drupal.org/project/fastly">Fastly Drupal module</a>).</p><p>There appears to be some confusion about what Fastly Soft Purge is, when it is useful, and how to use it. This post will help clear this topic up.</p><h2 id="what-is-fastly-soft-purge">What is Fastly Soft Purge?</h2><p>In Varnish, to force a miss for an object in cache you have a couple of options</p><ul><li>evict it completely (e.g. <code>PURGE</code>, or <code>BAN</code> with a lurker). The <a href="https://varnish-cache.org/docs/trunk/users-guide/purging.html">Varnish documentation cover this</a>. This is the same as Fastly Instant Purge.</li><li>or simply mark the object as expired. I imagine Fastly achieve this by tinkering with the TTL of the cached object. This is what Fastly Soft Purge is.</li></ul><p>So the main difference with Fastly Soft Purge is that the object persists in cache, and can be used in a stale context. This can prove to be extremely useful.</p><h2 id="when-is-fastly-soft-purge-useful">When is Fastly Soft Purge useful?</h2><p>I would say <strong>Fastly Soft Purge should be used in 99.99% of all purges</strong>. There are extremely limited situations in which I would recommend Fastly Instant Purge:</p><ul><li>You accidentally published embargoed content, or there is some legal issue with the content</li><li>You accidentally published incorrect or incomplete content</li><li>You accidentally published test content (we have all been there)</li><li>You need to destroy the entire cache for a given Fastly service &#x1F631; (which I never really recommend)</li></ul><p>It boils down to making a whoopsie really, and I would imagine the vast majority of your purges should (hopefully) not fall into these categories.</p><p>Having a stale object is cache is useful as it:</p><ul><li>soft purges to high traffic pages (e.g. the homepage) will not result in slow downs for new users, this is because &quot;Stale while revalidate&quot; will perform a background (asynchronous) refresh of the cached object, and will serve the cached stale object in the meantime.</li><li>if your origin is completely down (or in maintenance) stale objects can be served to users (which is often better than a generic error page). This is the &quot;Stale if error&quot; feature.</li></ul><h2 id="how-to-use-fastly-soft-purge-with-the-drupal-module">How to use Fastly Soft Purge with the Drupal module</h2><p>Just because you execute a purge to Fastly does not mean you will instantly get any benefit, you also need to combo this with some &quot;Stale Content Options&quot; to which I will go through.</p><p>If you are using the Fastly Drupal module, Soft Purge is configured like so:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2021/12/purge.png" class="kg-image" alt="Fastly Soft Purge vs Instant Purge - what is the difference?" loading="lazy" width="1882" height="1012" srcset="https://www.pixelite.co.nz/content/images/size/w600/2021/12/purge.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2021/12/purge.png 1000w, https://www.pixelite.co.nz/content/images/size/w1600/2021/12/purge.png 1600w, https://www.pixelite.co.nz/content/images/2021/12/purge.png 1882w" sizes="(min-width: 1200px) 1200px"><figcaption>The &quot;Purge options&quot; administration page in the Fastly Drupal module where you configure soft purging.</figcaption></figure><p>And as for Stale Content Options, you need to ensure that both &quot;Stale while revalidate&quot; and &quot;Stale if error&quot; are enabled. These are some basic tuning for the values as well:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2021/12/stale-1.png" class="kg-image" alt="Fastly Soft Purge vs Instant Purge - what is the difference?" loading="lazy" width="1818" height="970" srcset="https://www.pixelite.co.nz/content/images/size/w600/2021/12/stale-1.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2021/12/stale-1.png 1000w, https://www.pixelite.co.nz/content/images/size/w1600/2021/12/stale-1.png 1600w, https://www.pixelite.co.nz/content/images/2021/12/stale-1.png 1818w" sizes="(min-width: 1200px) 1200px"><figcaption>The &quot;Stale content options&quot; administration page in the Fastly Drupal module.</figcaption></figure><p>If you are not using Drupal (or using the Acquia purge module), then you can always set the above HTTP headers manually. See the <a href="https://docs.fastly.com/en/guides/serving-stale-content#manually-enabling-serve-stale">Fastly documentation on this</a>.</p><h2 id="how-to-use-fastly-soft-purge-with-the-api-only">How to use Fastly Soft Purge with the API only</h2><p>If you don&apos;t run Drupal, or just want to execute a soft purge via the Fastly API, then this is simple as well, just pass in the <code>Fastly-Soft-Purge</code> HTTP header:</p><pre><code class="language-bash">curl -sXPOST https://api.fastly.com/service/SERVICE_ID/purge/TAG \
 -H &apos;Fastly-Soft-Purge:1&apos; \
 -H &quot;Fastly-Key:$FASTLY_TOKEN&quot; | jq
{
  &quot;status&quot;: &quot;ok&quot;,
  &quot;id&quot;: &quot;10427-1615460322-6&quot;
}</code></pre><h2 id="wait-i-want-to-learn-more">Wait, I want to learn more</h2><p>If you want to go deeper in Varnish (what powers Fastly), and how objects, TTL, stale and grace (stale-while-revalidate) are involved, please <a href="https://info.varnish-software.com/blog/how-to-set-and-override-ttl">see their documentation</a>.</p><p>There is also this excellent graphic to which explains how an object goes from fresh to stale</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2021/12/ttl.png" class="kg-image" alt="Fastly Soft Purge vs Instant Purge - what is the difference?" loading="lazy" width="1600" height="540" srcset="https://www.pixelite.co.nz/content/images/size/w600/2021/12/ttl.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2021/12/ttl.png 1000w, https://www.pixelite.co.nz/content/images/2021/12/ttl.png 1600w" sizes="(min-width: 1200px) 1200px"><figcaption>Cached object timeline and how they interact</figcaption></figure><h2 id="gotchas">Gotchas</h2><p>If you use Fastly shielding (which you probably should), then there is the potential for soft purged content on the edge PoP to end up being re-cached as fresh &#x1F631;. <a href="https://developer.fastly.com/learning/concepts/purging/#stale-content-becoming-fresh">See the Fastly documentation</a> on this. There is a <a href="https://developer.fastly.com/learning/concepts/stale/#shielding-considerations">VCL snippet to mitigate this</a>:</p><figure class="kg-card kg-code-card"><pre><code class="language-vcl">if (fastly.ff.visits_this_service &gt; 0) {
  set req.max_stale_while_revalidate = 0s;
}</code></pre><figcaption><code>vcl_recv</code> snippet to mitigate soft purged content being re-cached as fresh when shielding is enabled in Fastly.</figcaption></figure><p>If you are not already using <a href="https://developer.fastly.com/learning/concepts/shielding/">Fastly shielding</a>, and you care about caching, and cache hit rates, you should really start to look at this. The benefits far outweigh the tiny edge cases.</p><p><strong>P.S.</strong> if you are not already using the <a href="https://docs.fastly.com/en/guides/authenticating-api-purge-requests">Fastly purge auth snippet</a>, get this installed ASAP. By not doing this, a bad actor is able to purge any single URL on your site, in an instant fashion. This could create a DoS.</p><figure class="kg-card kg-code-card"><pre><code class="language-vcl"># Fastly&apos;s URL purge feature allows you to purge individual URLs on your
# website. By default, authentication is not required to purge a URL with the
# Fastly API, but you can enable API token authentication with this snippet.
#
# @see https://docs.fastly.com/en/guides/authenticating-api-purge-requests
if ( req.request == &quot;FASTLYPURGE&quot; ) {
    set req.http.Fastly-Purge-Requires-Auth = &quot;1&quot;;
}</code></pre><figcaption><code>vcl_recv</code> snippet to enable single path URL purge with authentication in Fastly.</figcaption></figure><h2 id="comments">Comments</h2><p>If you have any other pro tips when it comes to soft purging in Fastly, please let me know.</p>]]></content:encoded></item><item><title><![CDATA[How many JOINs is too many? Tuning optimizer_search_depth for MySQL with Drupal]]></title><description><![CDATA[Drupal is extremely flexible, and creates a highly normalised table structure, 2 tables per field on a piece of content. We were seeing SQL queries appear to never complete when they had lots of JOINs in them.]]></description><link>https://www.pixelite.co.nz/article/when-is-too-many-joins-a-tuning-story-for-mysql-with-drupal/</link><guid isPermaLink="false">60def49afc441200063df1e9</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[MySQL]]></category><category><![CDATA[Performance]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sat, 13 Mar 2021 23:33:20 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2021/03/adam-niescioruk-NeuMYP6td1E-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2021/03/adam-niescioruk-NeuMYP6td1E-unsplash.jpg" alt="How many JOINs is too many? Tuning optimizer_search_depth for MySQL with Drupal"><p></p><h2 id="background">Background</h2><p>I was recently part of a migration from AWS (<a href="https://aws.amazon.com/rds/aurora/mysql-features/#High_Performance_and_Scalability">Amazon Aurora - MySQL-Compatible</a>) to Azure (<a href="https://azure.microsoft.com/en-us/services/mariadb/">Azure database for MariaDB 10.3</a>) for a large suite of applications. This platform contains a number of <a href="https://www.drupal.org/">Drupal 8</a> sites, which surface content through <a href="https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/api-overview">JSONAPI</a> (now part of core in Drupal 8).</p><h2 id="the-issue">The issue</h2><p>Drupal is extremely flexible, and creates a <a href="https://en.wikipedia.org/wiki/Database_normalization">highly normalised</a> table structure, 2 tables per field on a piece of content (1 for revisions, and another for active data). A given piece of content can contain dozens of fields. When loading a piece of content, it is not uncommon in Drupal to have 20+ joins on a single SQL query.</p><p>These SQL queries the end developers do not write by hand, Drupal abstracts this detail away through the <a href="https://www.drupal.org/docs/drupal-apis/entity-api/working-with-the-entity-api">entity API</a>.</p><p>We were seeing SQL queries appear to never complete when they had lots of JOINs in them. One query I found that was &apos;stuck&apos; had <strong>53 joins</strong>. In saying that, the database overall data size was tiny, with only 228 pieces of content in Drupal (this is very low, some Drupal sites can have millions of items of content).<br><br>Running a <code>SHOW FULL PROCESSLIST</code> showed these queries were all stuck in a <code>Statistics</code> phase.</p><p>The issue seemed to disproportionately impact SQL queries with &gt; 40 joins in a single query.<br><br>These queries did not appear to ever complete, and the CPU was pegged at 100%.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2021/02/azure_cpu.png" class="kg-image" alt="How many JOINs is too many? Tuning optimizer_search_depth for MySQL with Drupal" loading="lazy" width="2000" height="398" srcset="https://www.pixelite.co.nz/content/images/size/w600/2021/02/azure_cpu.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2021/02/azure_cpu.png 1000w, https://www.pixelite.co.nz/content/images/size/w1600/2021/02/azure_cpu.png 1600w, https://www.pixelite.co.nz/content/images/size/w2400/2021/02/azure_cpu.png 2400w" sizes="(min-width: 1200px) 1200px"><figcaption>CPU (in blue) being pegged at 100%</figcaption></figure><p>Upsizing the vCPU count in the database cluster had no impact, as the &apos;stuck&apos; queries just consume all the CPU available. We even had trouble trying to connect to the database cluster via the MySQL client, due to timeouts.</p><h2 id="research">Research</h2><p>After seeing the seeing queries stuck in <code>Statistics</code> phase, we did some digging to see what other content had to say on this topic:</p><ul><li><a href="https://dev.mysql.com/doc/refman/8.0/en/general-thread-states.html">https://dev.mysql.com/doc/refman/8.0/en/general-thread-states.html</a> seemed to indicate that <code>Statistics</code> phase hanging could be due to a lack of IOPS</li><li><a href="https://stackoverflow.com/questions/17797191/sql-query-stuck-in-statistics-state">https://stackoverflow.com/questions/17797191/sql-query-stuck-in-statistics-state</a> gave the first clue about <code>optimizer_search_depth</code> and how this can blow out query times</li><li><a href="https://www.drupal.org/project/ubercart/issues/636574">https://www.drupal.org/project/ubercart/issues/636574</a> indicated that tuning <code>optimizer_search_depth</code> to a value lower than <code>62</code> would be a good idea.</li><li><a href="https://mariadb.com/resources/blog/setting-optimizer-search-depth-in-mysql/">https://mariadb.com/resources/blog/setting-optimizer-search-depth-in-mysql/</a> contains really useful information on tuning <code>optimizer_search_depth</code> in general</li><li><a href="https://lists.mysql.com/internals/37635">https://lists.mysql.com/internals/37635</a> has the background on why <code>62</code> was chosen as the default for <code>optimizer_search_depth</code></li></ul><h2 id="actions-we-did">Actions we did</h2><p>After reading the above literature, we ended up settling on the &apos;automatic&apos; tuning for <code>optimizer_search_depth</code>. </p><pre><code class="language-yaml"> optimizer_search_depth = 0</code></pre><p>This will mean that queries that do &gt; 7 JOINS may not run the best query path, but at least they will actually complete. This is a win in my books.</p><p>I also killed every running query manually that was stuck in the <code>Statistics</code> phase. This brought down the CPU. The CPU never went up to the same levels due to the change in <code>optimizer_search_depth</code>.</p><h2 id="result-and-final-thoughts">Result and final thoughts</h2><p>The 53 JOIN query that used to fail to complete, now completes in 268ms. Not fast, but a damn sight faster than several days.</p><p>I still don&apos;t have a good explanation as to why the 53 join query had no issues executing on AWS Aurora MySQL, <code>optimizer_search_depth</code> is set to the default of <code>62</code> on there. I assume (like most of Aurora) that there is some special AWS sauce helping here. If anyone can shed more information on this, please let me know in the comments.</p>]]></content:encoded></item><item><title><![CDATA[Finding and killing anonymous sessions in Drupal]]></title><description><![CDATA[Sessions in Drupal are created when you log into Drupal. You can however start a session without first authenticating. This is known as an anonymous session]]></description><link>https://www.pixelite.co.nz/article/finding-and-killing-anonymous-sessions-in-drupal/</link><guid isPermaLink="false">60def49afc441200063df1e7</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Caching]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sun, 08 Nov 2020 08:39:00 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2020/11/hudson-hintze-p9EQ5Uqny5I-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2020/11/hudson-hintze-p9EQ5Uqny5I-unsplash.jpg" alt="Finding and killing anonymous sessions in Drupal"><p>Ordinarily, sessions in Drupal are created when you log into Drupal, e.g. when you want to create content, or perform some other function that requires authentication.</p><p>You can however (just like in any PHP application) start a session without first authenticating. This is known as an <em>anonymous session</em>.</p><p>This is <em>almost always a poor decision</em>, because as soon as you start a session, this generates a session cookie, and all reverse proxy caches will refuse to cache the responses from Drupal. Drupal will also add caching headers to indicate the response is not cacheable as well.</p><p>There are often better places to store state for anonymous users, e.g. the browsers <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">localStorage</a>, these have the advantage of not interfering with Drupal or caching.</p><p>This post will attempt to highlight some paths and avenues you can explore, the next time you come across this in an application you are working on.</p><h2 id="known-contributed-module-offenders">Known contributed module offenders</h2><p>Acquia <a href="https://docs.acquia.com/cloud-platform/develop/drupal/module-incompatibilities/">publishes a fairly broad list of modules</a> to which are known to break caching, some examples include</p><ul><li><a href="https://www.drupal.org/project/global_filter">global_filter</a></li><li><a href="https://www.drupal.org/project/flag">flag</a> (when voting as anonymous users is allowed)</li><li><a href="https://www.drupal.org/project/textsize">textsize</a></li><li><a href="https://www.drupal.org/project/ip_geoloc">ip_geoloc</a></li><li><a href="https://www.drupal.org/project/smart_ip">smart_ip</a></li></ul><p>If you are looking to store state (e.g. flag, textsize) then localStorage is better suited to that. If you are doing geolocation based things, most CDNs have the ability to do this lookup for you.</p><p>If you are using these modules, use with caution, and ensure you are not breaking the caching of your entire site.</p><h2 id="known-drupal-core-offenders">Known Drupal core offenders</h2><p>If you set a message using <code>drupal_set_message()</code> (in <a href="https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_set_message/7.x">Drupal 7</a>) or <code>\Drupal::messenger()-&gt;addStatus();</code> (in <a href="https://www.drupal.org/node/2774931">Drupal 8/9</a>) this will store the message in a PHP session. What sounds like a cool idea at the time - e.g. &quot;show a message to all users on the homepage&quot; - will end up making the site completely un-cacheable.</p><p>This can (and has) taken down high traffic sites.</p><h2 id="custom-code">Custom code</h2><p>Custom code is another place you will find code that may set anonymous sessions. Doing a quick grep will speed things up:</p><pre><code class="language-bash">grep -nrI --exclude-dir=web/vendor --exclude-dir=web/core &apos;$_SESSION&apos; web/</code></pre><h2 id="check-the-sessions-table">Check the <code>sessions</code> table</h2><p>It is fairly easy to spot the culprit when you examine the <code>sessions</code> table in Drupal&apos;s database. Here is a quick query to show the current count of anonymous sessions. This number ideally should be 0 or close to it.</p><pre><code class="language-sql">MySQL [example]&gt; SELECT count(*) FROM sessions WHERE uid = 0;
+----------+
| count(*) |
+----------+
|    10853 |
+----------+
1 row in set (0.004 sec)</code></pre><p>If you see you have a lot of sessions, then the next step is to read the contents of one:</p><pre><code class="language-sql">MySQL [example]&gt; SELECT SUBSTR(session, 1, 650) as &apos;&apos; FROM sessions WHERE uid = 0 ORDER BY timestamp DESC LIMIT 1;

_symfony_flashes|a:1:{s:5:&quot;error&quot;;a:149:{i:0;O:25:&quot;Drupal\Core\Render\Markup&quot;:1:{s:9:&quot; * string&quot;;s:2078:&quot;&lt;em class=&quot;placeholder&quot;&gt;Notice&lt;/em&gt;: Object of class Drupal\Core\Field\FieldItemList could not be converted to int in &lt;em class=&quot;placeholder&quot;&gt;example_preprocess_node()&lt;/em&gt; (line &lt;em class=&quot;placeholder&quot;&gt;146&lt;/em&gt; of &lt;em class=&quot;placeholder&quot;&gt;themes/example/example.theme&lt;/em&gt;). &lt;pre class=&quot;backtrace&quot;&gt;example_preprocess_node(Array, &amp;#039;node&amp;#039;, Array) (Line: 287)
Drupal\Core\Theme\ThemeManager-&amp;gt;render(&amp;#039;node&amp;#039;, Array) (Line: 437)
Drupal\Core\Render\Renderer-&amp;gt;doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer-&amp;gt;render(Array, ) (Line: 

1 row in set (0.001 sec)</code></pre><p>So now you know that there is a bug in the code <code>example_preprocess_node()</code> around line 146. This is the source of the messages in the first place.</p><p>After reading this <a href="https://drupal.stackexchange.com/a/281301/166">great answer on stackoverflow</a> this is likely due to the fact that the messages block is not rendered in the theme.</p><p>After checking the block layout page, the block is listed there</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2020/11/image.png" class="kg-image" alt="Finding and killing anonymous sessions in Drupal" loading="lazy" width="1332" height="260" srcset="https://www.pixelite.co.nz/content/images/size/w600/2020/11/image.png 600w, https://www.pixelite.co.nz/content/images/size/w1000/2020/11/image.png 1000w, https://www.pixelite.co.nz/content/images/2020/11/image.png 1332w" sizes="(min-width: 720px) 720px"><figcaption>The Drupal core messages block attempting to render in the <code>highlighted</code> region.</figcaption></figure><p>However checking the page templates, the variable <code>{{ page.highlighted }}</code> was <em>completely omitted</em>. This was the root cause of the messages not being able to sent to the browser, and thus they keep piling up in the <code>sessions</code> table.</p><p>If you want to see how much data you are storing for these sessions:</p><pre><code class="language-sql">MySQL [example]&gt; SELECT uid, CONCAT(SUM(LENGTH(session)) / 1048576.0, &apos; MB&apos;) as size FROM sessions  WHERE uid = 0 GROUP BY uid;
+-----+--------------+
| uid | size         |
+-----+--------------+
|   0 | 2433.6637 MB |
+-----+--------------+
1 row in set (0.513 sec)</code></pre><p>Around 2.4GB for this one site &#x1F631;. That is a lot of messages.</p><h2 id="comments">Comments</h2><p>If you have any other tips and tricks for hunting down anonymous sessions, or even any (anonymized) war stories - please let me know in the comments.</p>]]></content:encoded></item><item><title><![CDATA[Preparing for a high traffic event, simple steps to success]]></title><description><![CDATA[The steps that any new launch or high traffic event should go through in order to have the best chance of success. ]]></description><link>https://www.pixelite.co.nz/article/preparing-for-a-high-traffic-event-simple-steps-to-success/</link><guid isPermaLink="false">60def49afc441200063df1db</guid><category><![CDATA[Performance]]></category><category><![CDATA[Caching]]></category><category><![CDATA[Hosting]]></category><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Mon, 02 Dec 2019 09:34:49 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/11/denys-nevozhai-7nrsVjvALnA-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/11/denys-nevozhai-7nrsVjvALnA-unsplash.jpg" alt="Preparing for a high traffic event, simple steps to success"><p>The steps that any new launch or high traffic event should go through in order to have the best chance of success. This post is aimed at the project management level, so will try to stay out of the weeds, and focus on the high level topics you need to think about. There is a ~18 minute recording at the end of this post where I presented this topic at <a href="https://drupalsouth.org/">Drupalsouth 2019</a>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/Drupalsouth_2019_Hobart_-_Google_Slides.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>Title slide from the presentation.</figcaption></figure><h2 id="preamble-what-could-be-considered-a-high-traffic-event">Preamble: What could be considered a high traffic event</h2><p>Launching a brand new site</p><ul><li>Re-platforming (e.g. moving CMS version or type, or between hosting providers)</li><li>eDM or other marketing event (e.g. Adwords)</li><li>Planned traffic event (e.g. black Friday)</li><li>Unplanned traffic event (e.g. news and media site)</li></ul><h3 id="step-1-ensure-you-have-some-basic-drupal-configuration-in-place">Step 1) Ensure you have some basic Drupal configuration in place</h3><ul><li>Disable known problem child modules <code>dblog</code>, <code>devel</code>, <code>statistics</code>, <code>radioactivity</code>, <code>page_cache</code></li><li>Enable <code>dynamic_page_cache</code> (if you have authenticated traffic)</li><li>Set minimum cache lifetime to something sensible</li><li>JS and CSS aggregation enabled</li><li>Automate these checks with <a href="https://github.com/drutiny/drutiny">Drutiny</a></li></ul><h3 id="step-2-content-delivery-network-cdn-">Step 2) Content Delivery Network (CDN)</h3><p>Additional insurance against a lot of traffic is distributing your cached content to all corners of the globe.</p><p>Tiered caching should be used to ensure the highest offload rate. Most CDN providers will support this at a given price point.</p><h3 id="step-3-cache-tuning-and-minimising-origin-requests">Step 3) Cache tuning and minimising origin requests</h3><p>Every request that bypasses your CDN layer adds load to the platform. In order to have the best chance of surviving a high traffic event, origin traffic needs to be carefully considered and reduced where possible.</p><p>Requests to origin that are often overlooked</p><ul><li>404s</li><li>Marketing based parameters (e.g. <code>utm_campaign</code>)</li><li>Redirects (especially if re-platforming)</li><li>WAF to block silly requests (e.g. Wordpress URLs like <code>wp-login.php</code>)</li></ul><p>It you are interested in WAF tuning, you should check out my talk last year on <a href="https://youtu.be/oQizvm_dDeM">using Cloudflare to secure your Drupal site</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/ddos.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>This has happened to a customer of mine in the past. Fun fact the <code>gclid</code> and <code>dclid</code> query parameters are guaranteed unique for every user and click. This effectively makes them un-cacheable.</figcaption></figure><h3 id="step-4-load-testing">Step 4) Load testing</h3><p>If you are building a new site, or are expecting a substantially different traffic profile than what you have currently, then you should look to load test the system.</p><ul><li>Production hardware replica (scaled up if appropriate)</li><li>Emulate expected user behaviour, use existing analytics, or expected flows</li><li>Emulate what the browser would be doing (download all assets, including any HTTP 404s)</li><li>Ensure complex tasks are also simulated at the same time (e.g. editorial, searching, form submissions, feeds ingestions)</li></ul><p>At the end of this task (they you may need to run several times), you should have the confidence that you can handle the traffic expected.</p><h3 id="step-5-hardware-auto-scaling">Step 5) Hardware (auto) scaling</h3><p>Now that you have the hardware you need to have in place with load testing, ensure you have autoscaling in place to deal with the peaks and troughs (it is unlikely you need to run your peak hardware for the entire duration of the event).</p><p>Autoscaling can also help if the origin traffic that you experience is higher than anticipated.</p><p>Test the autoscaler, set limits that you are comfortable with, and ensure you know how quickly the new resources take to come to life.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/auto.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>Be nice to your OPs team, use an auto scaler.</figcaption></figure><h3 id="step-6-have-a-good-fallback">Step 6) Have a good fallback</h3><p>Say the worst does happen, and you site does go down, or a critical API drops off the face of the internet, what does the end user see? Can you offer at least a better experience than a generic web server error page?</p><p>Most CDNs will have the ability to load balance origins (hot DR), and even fallback to a static version of the site if all origins are down.</p><p>It would make sense to test this prior to the high load event as well.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/abc.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>ABC&apos;s news website went down just before Drupalsouth 2019, and someone managed to screencap it, and then send it to me. I am confident that you can come up with a better fallback than this error page.</figcaption></figure><h3 id="step-7-warm-your-cache">Step 7) Warm your cache</h3><p>If you have a rather long tail website, it will be worth warming your cache prior to the event. An excellent module called <a href="https://www.drupal.org/project/warmer">warmer</a> has been written, to which allows warming all sorts of caches. It can for instance load every page in the XML sitemap. So this is fairly low effort, high reward.</p><h3 id="step-8-third-party-api-dependencies">Step 8) Third party API dependencies</h3><p>This is more of a fundamental design decision likely made much earlier on in the project. Say the content of your page is <em>dependent</em> on the content in an API response. &#xA0;If you request the API content during page generation time, then you are tying the <em>speed</em> and <em>availability</em> of your site to another site (often outside your control).</p><p>This can lead to slow page load times, and worse case scenario can tie up your server&apos;s resources.</p><p>New Relic APM has &quot;external requests&quot; to which allow you to visualise this.</p><p>There are ways to mitigate this:</p><ul><li>Fetch the data in the background and cache locally in Drupal for as long as the data is considered &apos;good&apos;. e.g. using Drush and a cronjob.</li><li>Use a client side application (e.g. React) and request the API response in the client side</li><li>Use a CDN on the API and see Step #6 above</li></ul><h3 id="step-9-realtime-analytics">Step 9) Realtime analytics</h3><p>During the event, having access to realtime (or near realtime) analytics to find out</p><ul><li>how the system is currently performing</li><li>requests/sec</li><li>where the traffic is coming from</li><li>cache offload rate from the CDN</li></ul><p>Is extremely valuable. Even more valuable is being able to respond to this data in a quick and efficient matter. Having access to technical people can help. The types of logs and analytics you should be looking to get a hold of:</p><ul><li>Web analytics tools (e.g. Google Analytics)</li><li>APM tools (e.g. New Relic)</li><li>CDN analytics (e.g. Cloudflare Logs)</li><li>Log stream from hosting provider (e.g. PHP error log)</li></ul><p>To see where you can take this, you might also be interested in <a href="https://www.pixelite.co.nz/article/analyzing-cloudflare-logs-with-the-command-line/">reading this blog post</a> that shows off some dashboards that were purpose built for a high traffic event.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/dashboard.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>An example dashboard that was written for a previous high traffic event that I was involved with. The data is around 6 minutes delayed, but still proved invaluable.</figcaption></figure><h3 id="step-10-application-changes-in-a-pinch">Step 10) Application changes in a pinch</h3><p>If you do spot something in your analytics, knowing what tools you have at your disposal to mitigate issues quickly and easily is worth knowing.</p><ul><li>Cloudflare page rules (redirect a broken path, increase the WAF presence on a route)</li><li>Nginx or Apache configuration</li><li>Application hotfix (avoid clearing the cache)</li></ul><p>Knowing what tool will solve what problem, how long each option takes to deploy, how safe it is, how easy is the rollback is is absolutely critical.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/12/pagerules.png" class="kg-image" alt="Preparing for a high traffic event, simple steps to success" loading="lazy"><figcaption>Cloudflare&apos;s pagerules feature is an excellent way to make quick changes to how your application functions.</figcaption></figure><h3 id="step-11-letting-your-hosting-provider-and-their-support-team-know">Step 11) Letting your hosting provider and their support team know</h3><p>No-one likes surprises, so plan ahead. Ensure there are people available or on call during your traffic event. This goes for both your hosting provider, to CDN provider to support staff.</p><h2 id="postamble-what-success-looks-like">Postamble: What success looks like</h2><p>So after your high traffic event has ended, here are some simple things to check in order to see how successful you were:</p><ul><li>Minimal origin requests and a high CDN offload</li><li>Boring origin hardware graphs</li><li>No rants on twitter</li><li>No trending hashtag on twitter that is negative</li><li>Users remember the event for it&apos;s content, and not the problems with it</li></ul><h2 id="drupalsouth-2019-video">Drupalsouth 2019 video</h2><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="480" height="270" src="https://www.youtube.com/embed/zujP5cGJUFw?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><figcaption>This was me presenting this topic at Drupalsouth 2019.</figcaption></figure><p>Let me know in the comments if this was of use, and also if you have any other words of wisdom for anyone else.</p>]]></content:encoded></item><item><title><![CDATA[Search API attachments and storing reasonable amounts of data]]></title><description><![CDATA[Search API Attachments has a setting that allows you to store only the most important information in the database.]]></description><link>https://www.pixelite.co.nz/article/search-api-attachments-and-storing-reasonable-amounts-of-data/</link><guid isPermaLink="false">60def49afc441200063df1dc</guid><category><![CDATA[Drupal]]></category><category><![CDATA[MySQL]]></category><category><![CDATA[Search]]></category><category><![CDATA[Database]]></category><category><![CDATA[Drupal planet]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Wed, 20 Nov 2019 02:41:43 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/11/alexander-andrews-eNoeWZkO7Zc-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/11/alexander-andrews-eNoeWZkO7Zc-unsplash.jpg" alt="Search API attachments and storing reasonable amounts of data"><p>On a number of Drupal sites that I am involved with recently I see the error message when either trying to dump or restore certain databases:</p><pre><code class="language-sql">ERROR 2020 (HY000) at line 1: Got packet bigger than &apos;max_allowed_packet&apos;</code></pre><p>It is important to note that with MySQL there are 2 settings for <code>max_allowed_packet</code>, the client (what you are connecting from), and the server (what you are connecting to).</p><p>In order to find out the client&apos;s current settings, you can run:</p><pre><code class="language-bash">$ mysql --help | grep max-allowed-packet | grep -v &apos;#&apos; | awk &apos;{print $2/(1024*1024)}&apos;
16</code></pre><p>So <code>16MB</code> on the client side.</p><p>In order to find out the server&apos;s current setting, you can run:</p><pre><code class="language-sql">SHOW VARIABLES LIKE &apos;max_allowed_packet&apos;;
+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 67108864 |
+--------------------+----------+</code></pre><p>So <code>64MB</code> on the server side.</p><p>So at the moment:</p><ul><li>The client cannot send or receive more than <code>16MB</code> in a single statement</li><li>The server cannot send or receive more than <code>64MB</code> in a single statement</li></ul><p>So you can end up in a position where there is content in your database, that is working fine, but you cannot dump the database, nor can you restore from a dump (with your current client configuration).</p><p>Take the <a href="https://www.drupal.org/project/search_api_attachments">search_api_attachments</a> module in Drupal, it <a href="https://www.drupal.org/project/drupal/issues/2496457">uses the <code>key_value</code> table</a> in Drupal 8 to store extracted text from documents.</p><figure class="kg-card kg-code-card"><pre><code class="language-sql">SELECT name, length(value) as size FROM key_value ORDER BY size DESC limit 5;
+------------------------------+----------+
| name                         | size     |
+------------------------------+----------+
| search_api_attachments:14646 | 33623803 |
| search_api_attachments:14288 |  3394023 |
| search_api_attachments:13146 |  2356958 |
| search_api_attachments:4921  |  1554830 |
| search_api_attachments:1586  |  1549981 |
+------------------------------+----------+</code></pre><figcaption>You can see a 33MB document extracted into the <code>key_value</code> table</figcaption></figure><h2 id="solution-1-alter-max_allowed_packet">Solution 1 - alter max_allowed_packet</h2><p>The first solution is simply to alter the <code>max_allowed_packet</code> size to accommodate the size needed (on both the client and server). The only issue is that this is a game of cat and mouse. As soon as you tune the size to be larger, a content editor will upload a larger document.</p><p>It also means your database size will grow fairly unchecked, especially if you have heavy editorial where documents are commonly uploaded.</p><p>While I do advocate for sensible defaults, I think having &gt; <code>64MB</code> in a single cell in a table as potentially overkill for the benefits it provides.</p><h2 id="solution-2-reduce-the-amount-in-the-database">Solution 2 - reduce the amount in the database</h2><p>The point of indexing attachments is to ensure that you can still find content that is buried in binary files. I would argue that most of the important keywords of these documents will be in the first few pages. Indexing the entire document often will provide limited added value (if any).</p><p>Reading through the code of the <code>search_api_attachments</code> module, I spot these <a href="https://git.drupalcode.org/project/search_api_attachments/blob/8.x-1.x/src/Plugin/search_api/processor/FilesExtractor.php#L381-403">useful lines</a>:</p><pre><code class="language-php">  /**
   * Limit the indexed text to first N bytes.
   *
   * @param string $extracted_text
   *   The hole extracted text.
   *
   * @return string
   *   The first N bytes of the extracted text that will be indexed and cached.
   */
  public function limitBytes($extracted_text) {
    $bytes = 0;
    if (isset($this-&gt;configuration[&apos;number_first_bytes&apos;])) {
      $bytes = Bytes::toInt($this-&gt;configuration[&apos;number_first_bytes&apos;]);
    }
    // If $bytes is 0 return all items.
    if ($bytes == 0) {
      return $extracted_text;
    }
    else {
      $extracted_text = mb_strcut($extracted_text, 0, $bytes);
    }
    return $extracted_text;
  }</code></pre><p>So it turns out, baked into the module, is a way to effectively limit the number of characters stored in the database (<a href="https://www.drupal.org/project/search_api_attachments/issues/2888827">drupal.org issue</a>). </p><p>In order to enable this feature, you need to edit the Processors for a given index:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/11/index_list-3.png" class="kg-image" alt="Search API attachments and storing reasonable amounts of data" loading="lazy"><figcaption>Search API index lis, and the Processors link</figcaption></figure><p>Inside this page, is a configuration form that allows you to set a max limit for the amount stored in the database.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/11/processor.png" class="kg-image" alt="Search API attachments and storing reasonable amounts of data" loading="lazy"><figcaption>Search API Attachments &quot;limit size&quot; feature</figcaption></figure><p>After setting the size to <code>100 KB</code> which seems like a reasonable number, and then re-indexing the appropriate index, you see the results</p><pre><code class="language-sql">SELECT name, length(value) as size FROM key_value ORDER BY size DESC limit 5;
+--------------------------------+--------+
| name                           | size   |
+--------------------------------+--------+
| node.field_storage_definitions | 116308 |
| search_api_attachments:831     | 102412 |
| search_api_attachments:1536    | 102412 |
| search_api_attachments:1571    | 102412 |
| search_api_attachments:1576    | 102412 |
+--------------------------------+--------+</code></pre><p>So this will mean that the database is a lot smaller, allowing faster database backups, restores, and rollbacks. It also will save a lot of issues around forever tuning <code>max_allowed_packet</code>.</p><p>It is also worth noting that this <code>key_value</code> storage is actually a caching system for <code>search_api_attachments</code>, and that this table will be used, even if your only search servers are external to Drupal (e.g. Solr), and you make no use of database searching.</p><h2 id="update-21-november-2019">Update 21 November 2019</h2><p>In the hopes that the module maintainers make a more sensible default limit I have also raised <a href="https://www.drupal.org/project/search_api_attachments/issues/3095538">this issue</a> to get a default value set. Having any size limit would be better than having no limit. A patch is now uploaded so you can test this out.</p><h2 id="update-23-november-2019">Update 23 November 2019</h2><p>The <a href="https://git.drupalcode.org/project/search_api_attachments/commit/84ca55f">patch has been committed</a>, and a <a href="https://www.drupal.org/project/search_api_attachments/releases/8.x-1.0-beta15">new beta released</a> for Drupal 8 &#x1F389;. Thanks so much to <a href="https://www.drupal.org/u/izus">Ismaeil</a> (module maintainer) for the prompt reply and action.</p><h2 id="comments">Comments</h2><p>If you have come across this in the past, what was your go to solution? I am keen to understand how others have solved this issue, and how big your <code>key_value</code> table got in the mean time.</p>]]></content:encoded></item><item><title><![CDATA[How to add sub tabs under the User profile in Drupal 8]]></title><description><![CDATA[A simple step by step tutorial on adding a custom sub tab in a user's profile in Drupal 8.]]></description><link>https://www.pixelite.co.nz/article/how-to-add-sub-tabs-under-the-user-profile-in-drupal-8/</link><guid isPermaLink="false">60def49afc441200063df1da</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Drupal console]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Thu, 26 Sep 2019 22:18:49 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/09/mateo-avila-chinchilla-x_8oJhYU31k-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/09/mateo-avila-chinchilla-x_8oJhYU31k-unsplash.jpg" alt="How to add sub tabs under the User profile in Drupal 8"><p>I am writing this quick tutorial in the hopes it helps someone else out there. There are a few guides out there to <a href="https://drupal.stackexchange.com/questions/275753/how-do-i-add-secondary-tabs-to-the-user-profile-edit-tab">do similar tasks</a> to this. They just are not quite what I wanted. </p><p>To give everyone an idea on the desired outcome, this is what I wanted to achieve:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/09/example_sub_tab.png" class="kg-image" alt="How to add sub tabs under the User profile in Drupal 8" loading="lazy"><figcaption>Example user profile with 2 custom tabs in it.</figcaption></figure><p>Before I dive into this, I will mention that you can do this with views, if all that you want to produce is content supplied by views. Ivan <a href="https://www.webwash.net/custom-tab-user-profile-page-views-drupal-8/">wrote a nice article on this</a>. In my situation, I wanted a completely custom route, controller and theme function. I wanted full control over the output.</p><h2 id="steps-to-add-sub-tabs">Steps to add sub tabs</h2><h3 id="step-1-create-a-new-module">Step 1 - create a new module</h3><p>If you don&apos;t already have a module to house this code, you will need one. These commands make use of <a href="https://drupalconsole.com/articles/how-to-install-drupal-console">Drupal console</a>, so ensure you have this installed first.</p><pre><code class="language-bash">drupal generate:module --module=&apos;Example module&apos; --machine-name=&apos;example&apos; --module-path=&apos;modules/custom&apos; --description=&apos;My example module&apos; --package=&apos;Custom&apos; --core=&apos;8.x&apos;</code></pre><h3 id="step-2-create-a-new-controller">Step 2 - create a new controller</h3><p>Now that you have a base module, you need a route</p><pre><code class="language-bash">drupal generate:controller --module=&apos;example&apos; --class=&apos;ExampleController&apos; --routes=&apos;&quot;title&quot;:&quot;Content&quot;, &quot;name&quot;:&quot;example.user.contentlist&quot;, &quot;method&quot;:&quot;contentListUser&quot;, &quot;path&quot;:&quot;/user/{user}/content&quot;&apos;</code></pre><h3 id="step-3-alter-your-routes">Step 3 - alter your routes</h3><p>In order to use magic autoloading, and also proper access control, you can alter your routes to look like this. This is covered in the <a href="https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes">official documentation</a>.</p><pre><code class="language-php"># Content user tab.
example.user.contentlist:
  path: &apos;/user/{user}/content&apos;
  defaults:
    _controller: &apos;\Drupal\example\Controller\ExampleController::contentListUser&apos;
    _title: &apos;Content&apos;
  requirements:
    _permission: &apos;access content&apos;
    _entity_access: &apos;user.view&apos;
    user: \d+
  options:
    parameters:
      user:
        type: entity:user

# Reports user tab.
example.user.reportList:
  path: &apos;/user/{user}/reports&apos;
  defaults:
    _controller: &apos;\Drupal\example\Controller\ExampleController::reportListUser&apos;
    _title: &apos;Reports&apos;
  requirements:
    _permission: &apos;access content&apos;
    _entity_access: &apos;user.view&apos;
    user: \d+
  options:
    parameters:
      user:
        type: entity:user</code></pre><h3 id="step-4-create-example-links-task-yml">Step 4 - create <code>example.links.task.yml</code></h3><p>This is the code that actually creates the tabs in the user profile. No Drupal console command for this unfortunately. The key part of this is defining <code>base_route: entity.user.canonical</code>.</p><pre><code class="language-yaml">example.user.content_task:
  title: &apos;Content&apos;
  route_name: example.user.contentlist
  base_route: entity.user.canonical
  weight: 1

example.user.reports_task:
  title: &apos;Reports&apos;
  route_name: example.user.reportList
  base_route: entity.user.canonical
  weight: 2</code></pre><h3 id="step-5-enable-the-module">Step 5 - enable the module</h3><p>Don&apos;t forget to actually turn on your custom module, nothing will work until then.</p><pre><code class="language-bash">drush en example</code></pre><h2 id="example-module">Example module</h2><p>The best (and simplest) example module I could find that demonstrates this is the <a href="https://github.com/drupal/drupal/tree/8.8.x/core/modules/tracker">Tracker module in Drupal core</a>. The Tracker module adds a tab to the user profile.</p>]]></content:encoded></item><item><title><![CDATA[How the feeds module in Drupal 7 ended up causing MySQL to sever the connection]]></title><description><![CDATA[This is a short story on an interesting problem we were having with the Feeds module and Feeds directory fetcher module in Drupal 7.]]></description><link>https://www.pixelite.co.nz/article/how-the-feeds-module-in-drupal-7-ended-up-causing-mysql-to-sever-the-connection/</link><guid isPermaLink="false">60def49afc441200063df1d9</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[SQL]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Mon, 19 Aug 2019 03:05:09 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/08/magnezis-magnestic-TW62wXQ6Omc-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/08/magnezis-magnestic-TW62wXQ6Omc-unsplash.jpg" alt="How the feeds module in Drupal 7 ended up causing MySQL to sever the connection"><p>This is a short story on an interesting problem we were having with the <a href="https://www.drupal.org/project/feeds">Feeds</a> module and <a href="https://www.drupal.org/project/feeds_fetcher_directory">Feeds directory fetcher</a> module in Drupal 7.</p><h2 id="background-on-the-use-of-feeds">Background on the use of Feeds</h2><p>Feeds for this site is being used to ingest XML from a third party source (Reuters). The feed perhaps ingests a couple of hundred articles per day. There can be updates to the existing imported articles as well, but typically they are only updated the day the article is ingested.</p><p>Feeds was working well for over a few years, and then all of a sudden, the ingests started to fail. The failure was only on production, whilst the other (lower environments) the ingestion worked as expected.</p><h2 id="the-bizarre-error">The bizarre error</h2><p>On production we were experiencing the error during import:</p><pre><code class="language-bash">PDOStatement::execute(): MySQL server has gone away database.inc:2227 [warning] 
PDOStatement::execute(): Error reading result set&apos;s header [warning] 
database.inc:2227PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has [error]</code></pre><p>The error is not so much that the database server is not alive, more so that PHP&apos;s connection to the database has been severed due to exceeding MySQL&apos;s <code>wait_timeout</code> value.</p><p>The reason why this would occur on only production happens on Acquia typically when you need to read and write to the shared filesystem a lot. As lower environments, the filesystem is local disk (as the environments are not clustered) the access is a lot faster. On production, the public filesystem is a network file share (which is slower).</p><h2 id="going-down-the-rabbit-hole">Going down the rabbit hole</h2><p>Working out <strong>why</strong> Feeds was wanting to read and/or write many files from the filesystem was the next question, and immediately one thing stood out. The shear size of the <code>config</code> column in the <code>feeds_source</code> table:</p><figure class="kg-card kg-code-card"><pre><code class="language-sql">mysql&gt; SELECT id,SUM(char_length(config))/1048576 AS size FROM feeds_source GROUP BY id;
+-------------------------------------+---------+
| id                                  | size    |
+-------------------------------------+---------+
| apworldcup_article                  |  0.0001 |
| blogs_photo_import                  |  0.0003 |
| csv_infographics                    |  0.0002 |
| photo_feed                          |  0.0002 |
| po_feeds_prestige_article           |  1.5412 |
| po_feeds_prestige_gallery           |  1.5410 |
| po_feeds_prestige_photo             |  0.2279 |
| po_feeds_reuters_article            | 21.5086 |
| po_feeds_reuters_composite          | 41.9530 |
| po_feeds_reuters_photo              | 52.6076 |
| example_line_feed_article           |  0.0002 |
| example_line_feed_associate_article |  0.0001 |
| example_line_feed_blogs             |  0.0003 |
| example_line_feed_gallery           |  0.0002 |
| example_line_feed_photo             |  0.0001 |
| example_line_feed_video             |  0.0002 |
| example_line_youtube_feed           |  0.0003 |
+-------------------------------------+---------+</code></pre><figcaption>What 52 MB of ASCII looks like in a single cell.</figcaption></figure><p>Having to deserialize 52 MB of ASCII in PHP is bad enough.</p><p>The next step was dumping the value of the <code>config</code> column for a single row:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">drush --uri=www.example.com sqlq &apos;SELECT config FROM feeds_source WHERE id = &quot;po_feeds_reuters_photo&quot;&apos; &gt; /tmp/po_feeds_reuters_photo.txt</code></pre><figcaption>Get the 55 MB of ASCII in a file for analysis</figcaption></figure><p>Then open the resulting file in vim:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&quot;/tmp/po_feeds_reuters_photo.txt&quot; 1L, 55163105C</code></pre><figcaption>Vim struggles to open any file that has 55 million characters on a single line</figcaption></figure><p>And sure enough, inside this <code>config</code> column was a reference to every single XML file ever imported, a cool ~450,000 files.</p><figure class="kg-card kg-code-card"><pre><code class="language-php">a:2:{s:31:&quot;feeds_fetcher_directory_fetcher&quot;;a:3:{s:6:&quot;source&quot;;s:23:&quot;private://reuters/pass1&quot;;s:5:&quot;reset&quot;;i:0;
s:18:&quot;feed_files_fetched&quot;;a:457065:{
s:94:&quot;private://reuters/pass1/topnews/2018-07-04T083557Z_1_KBN1JU0WQ_RTROPTC_0_US-CHINA-AUTOS-GM.XML&quot;;i:1530693632;
s:94:&quot;private://reuters/pass1/topnews/2018-07-04T083557Z_1_KBN1JU0WR_RTROPTT_0_US-CHINA-AUTOS-GM.XML&quot;;i:1530693632;
s:96:&quot;private://reuters/pass1/topnews/2018-07-04T083557Z_1_LYNXMPEE630KJ_RTROPTP_0_USA-TRADE-CHINA.XML&quot;;i:1530693632;
s:97:&quot;private://reuters/pass1/topnews/2018-07-04T083617Z_147681_KBE99T04E_RTROPTT-LNK_0_OUSBSM-LINK.XML&quot;;i:1530693632;
s:102:&quot;private://reuters/pass1/topnews/2018-07-04T083658Z_1_KBN1JU0X2_RTROPTT_0_JAPAN-RETAIL-ZOZOTOWN-INT.XML&quot;;i:1530693632</code></pre><figcaption>457,065 is the array size in <code>feed_files_fetched</code></figcaption></figure><p>So this is the root cause of the problem, Drupal is attempting to <code>stat()</code> ~450,000 files that do not exist, and these files are mounted on a network file share. This process took longer than MySQL&apos;s <code>wait_timeout</code> and MySQL closed the connection. When Drupal finally wanted to talk to the database, it was not to be found.</p><p>Interesting enough, the problem of the <a href="https://www.drupal.org/node/1715124">config column running out of space came up in 2012</a>, and &quot;the solution&quot; was just to change the type of the column. Now you can store 4GB of content in this 1 column. In hindsight, perhaps this was not the smartest solution.</p><p>Also in 2012, you see the<a href="https://www.drupal.org/project/feeds_fetcher_directory/issues/1630970#comment-6436920"> comment from @valderama</a>:</p><blockquote>However, as <code>feed_files_fetched</code> saves all items which were already imported, it grows endless if you have a periodic import.</blockquote><p>Great to see we are not the only people having this pain.</p><h2 id="the-solution">The solution</h2><p>The simple solution to limp by is to increase the <code>wait_timeout</code> value of your Database connection. This gives Drupal more time to scan for the previously imported files prior to importing the new ones.</p><figure class="kg-card kg-code-card"><pre><code class="language-php">$databases[&apos;default&apos;][&apos;default&apos;][&apos;init_commands&apos;] = [
  &apos;wait_timeout&apos; =&gt; &quot;SET SESSION wait_timeout=2500&quot;,
];</code></pre><figcaption>Increasing MySQL&apos;s <code>wait_timeout</code> in Drupal&apos;s <code>settings.php</code>.</figcaption></figure><p>As you might guess, this is not a good long term solution for sites with a lot of imported content, or content that is continually being imported.</p><p>Instead we opted to do a fairly quick update hook that would loop though all of the items in the <code>feed_files_fetched</code> key, and unset the older items.</p><pre><code class="language-php">&lt;?php

/**
 * @file
 * Install file.
 */

/**
 * Function to iterate through multiple strings.
 *
 * @see https://www.sitepoint.com/community/t/strpos-with-multiple-characters/2004/2
 * @param $haystack
 * @param $needles
 * @param int $offset
 * @return bool|int
 */
function multi_strpos($haystack, $needles, $offset = 0) {
  foreach ($needles as $n) {
    if (strpos($haystack, $n, $offset) !== FALSE) {
      return strpos($haystack, $n, $offset);
    }
  }
  return false;
}

/**
 * Implements hook_update_N().
 */
function example_reuters_update_7001() {
  $feedsSource = db_select(&quot;feeds_source&quot;, &quot;fs&quot;)
    -&gt;fields(&apos;fs&apos;, [&apos;config&apos;])
    -&gt;condition(&apos;fs.id&apos;, &apos;po_feeds_reuters_photo&apos;)
    -&gt;execute()
    -&gt;fetchObject();

  $config = unserialize($feedsSource-&gt;config);

  // We only want to keep the last week&apos;s worth of imported articles in the
  // database for content updates.
  $cutoff_date = [];
  for ($i = 0; $i &lt; 7; $i++) {
    $cutoff_date[] = date(&apos;Y-m-d&apos;, strtotime(&quot;-$i days&quot;));
  }

  watchdog(&apos;FeedSource records - Before trimmed at &apos; . time(), count($config[&apos;feeds_fetcher_directory_fetcher&apos;][&apos;feed_files_fetched&apos;]));

  // We attempt to match based on the filename of the imported file. This works
  // as the files have a date in their filename.
  // e.g. &apos;2018-07-04T083557Z_1_KBN1JU0WQ_RTROPTC_0_US-CHINA-AUTOS-GM.XML&apos;
  foreach ($config[&apos;feeds_fetcher_directory_fetcher&apos;][&apos;feed_files_fetched&apos;] as $key =&gt; $source) {
    if (multi_strpos($key, $cutoff_date) === FALSE) {
      unset($config[&apos;feeds_fetcher_directory_fetcher&apos;][&apos;feed_files_fetched&apos;][$key]);
    }
  }

  watchdog(&apos;FeedSource records - After trimmed at &apos; . time(), count($config[&apos;feeds_fetcher_directory_fetcher&apos;][&apos;feed_files_fetched&apos;]));

  // Save back to the database.
  db_update(&apos;feeds_source&apos;)
    -&gt;fields([
      &apos;config&apos; =&gt; serialize($config),
    ])
    -&gt;condition(&apos;id&apos;, &apos;po_feeds_reuters_photo&apos;, &apos;=&apos;)
    -&gt;execute();
}</code></pre><p>Before the code ran, there were &gt; 450,000 items in the array, and after we are below 100. So a massive decrease in database size.</p><p>More importantly, the importer now runs a lot quicker (as it is not scanning the shared filesystem for non-existent files).</p><h2 id="what-this-means-for-feeds-in-drupal-8">What this means for Feeds in Drupal 8</h2><p>It came to my attention from <a href="https://www.drupal.org/u/dinesh18">Dinesh</a>, that this same issue may likely impact Drupal 8 feeds. To make things slightly more interesting, is that the functionality of the Drupal 7 module <a href="https://www.drupal.org/project/feeds_fetcher_directory">feeds_fetcher_directory</a> is now moved into the <a href="https://git.drupalcode.org/project/feeds/blob/8.x-3.x/src/Feeds/Fetcher/DirectoryFetcher.php">main feeds module</a>.</p><p>An <a href="https://www.drupal.org/project/feeds/issues/3078213">issue has been opened on Drupal.org to track this</a>. I will update this blog post once we know the outcome.</p><h2 id="update-27-september-2019">Update - 27 September 2019</h2><p>The above update hook has been run on production (to where Gluster is used). Feeds used to take upwards of 30 minutes to run there (even if there was no new files to process). Post the update hook running, it is now under 1 minute. We were also able to remove the <code>wait_timeout</code> setting as well. So this is a nice result.</p>]]></content:encoded></item><item><title><![CDATA[PHP 7.3 and when you can upgrade your Drupal site]]></title><description><![CDATA[PHP 7.3.0 was released in December 2018, and brings with it a number of improvements in both performance and the language. As always with Drupal you need to strike a balance between adopting these new improvements early and running into issues]]></description><link>https://www.pixelite.co.nz/article/php-7-3-and-when-you-can-upgrade-your-drupal-site/</link><guid isPermaLink="false">60def49afc441200063df1d5</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Sun, 28 Jul 2019 19:59:38 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/07/peter-maselkowski-N135eczYTAs-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/07/peter-maselkowski-N135eczYTAs-unsplash.jpg" alt="PHP 7.3 and when you can upgrade your Drupal site"><p>PHP 7.3.0 was <a href="https://www.php.net/ChangeLog-7.php#7.3.0">released in December 2018</a>, and brings with it a number of improvements in both performance and the language. As always with Drupal you need to strike a balance between adopting these new improvements early and running into issues that are not yet fixed by the community.</p><h2 id="why-upgrade-php-to-7-3-over-7-2">Why upgrade PHP to 7.3 over 7.2?</h2><ul><li><strong>It is around 10% faster compared to PHP 7.2</strong> - some basic benchmarks for Drupal on <a href="https://kinsta.com/blog/php-benchmarks/#drupal-benchmarks">https://kinsta.com/blog/php-benchmarks/#drupal-benchmarks</a></li><li><strong>A bunch of quality of life improvements to the language</strong> - e.g. <a href="https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes">flexible heredoc and nowdoc syntaxes</a>, <a href="https://wiki.php.net/rfc/trailing-comma-function-calls">allowing a trailing comma in function calls</a> and <a href="https://wiki.php.net/rfc/json_throw_on_error">better JSON parsing error messages</a> (just to name a few). I would recommend reading this <a href="https://kinsta.com/blog/php-7-3/">great blog post on the topic</a> if you want to know more.</li></ul><h2 id="what-hosting-providers-support-php-7-3">What hosting providers support PHP 7.3?</h2><p>All the major players have support, here is how you configure it for each.</p><h3 id="acquia">Acquia</h3><p>Somewhere around April 2019 the option to choose PHP 7.3 was released. You can opt into this version by changing a value in Acquia Cloud. This can be done on a per environment basis.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/07/Overview___Acquia.png" class="kg-image" alt="PHP 7.3 and when you can upgrade your Drupal site" loading="lazy"><figcaption>The PHP version configuration screen for Acquia Cloud&#xA0;</figcaption></figure><h3 id="pantheon">Pantheon</h3><p>Pantheon have had support since the April 2019 as well (<a href="https://pantheon.io/blog/speed-your-wordpress-or-drupal-site-php-73">see the announcement post</a>). To change the version, you update your <code>pantheon.yml</code> file (<a href="https://pantheon.io/docs/php-versions/">see the docs on this</a>).</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml"># Put overrides to your pantheon.upstream.yml file here.
# For more information, see: https://pantheon.io/docs/pantheon-yml/
api_version: 1
php_version: 7.3</code></pre><figcaption>Example <code>pantheon.yml</code> file</figcaption></figure><p>On a side note, it is interesting that PHP 5.3 is still offered on Pantheon (end of life for <a href="https://www.php.net/eol.php">nearly 5 years</a>).</p><h3 id="platform-sh">Platform.sh</h3><p>Unsure when Platform.sh released PHP 7.3, but the process to enable it is very similar to Pantheon, you update your <code>.platform.app.yaml</code> file (<a href="https://docs.platform.sh/languages/php.html#supported-versions">see the docs on this</a>).</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml"># .platform.app.yaml
type: &quot;php:7.3&quot;</code></pre><figcaption>Example <code>.platform.app.yaml</code> file</figcaption></figure><h3 id="dreamhost">Dreamhost</h3><p>PHP 7.3 is also available on Dreamhost, and can be chosen in a dropdown in their UI (<a href="https://help.dreamhost.com/hc/en-us/articles/214895317-How-do-I-change-the-PHP-version-of-my-site-">see the docs on this</a>).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/07/02_PHP.fw.png" class="kg-image" alt="PHP 7.3 and when you can upgrade your Drupal site" loading="lazy"><figcaption>The &apos;<em>Manage Domains&apos; section of Dreamhost</em></figcaption></figure><p>Dreamhost also win some award for also allowing the oldest version of PHP that I have seen in a while (PHP 5.2).</p><h2 id="when-can-you-upgrade-php-7-3">When can you upgrade PHP 7.3</h2><h3 id="drupal-8">Drupal 8</h3><p>As of <a href="https://www.drupal.org/project/drupal/releases/8.6.4">Drupal 8.6.4</a> (6<sup>th</sup> December 2018), PHP 7.3 is fully supported in Drupal core (<a href="https://www.drupal.org/node/3038583">change record</a>). I have been running PHP 7.3 with Drupal 8 for a while now and have seen no issues, and this includes running some complex installation profiles such as <a href="https://www.drupal.org/project/thunder">Thunder</a> and <a href="https://www.drupal.org/project/lightning">Lightning</a>.</p><p>Any Drupal 8 site that is reasonably up to date, should be fine with PHP 7.3.</p><h3 id="drupal-7">Drupal 7</h3><p>Slated for support in the next release of Drupal 7 - being Drupal 7.68 (see the <a href="https://www.drupal.org/project/drupal/issues/3012308">drupal.org issue</a>), however there are a number of related tasks that seem like deal breakers. There also is <a href="https://www.drupal.org/node/3060/qa">not PHP 7.3 and Drupal 7 tests running</a> daily either.</p><p>It seems like for the mean time, it is probably best to hold off on the PHP 7.3 upgrade until 7.68 is out the door, and also contributed modules have had a chance to upgrade and release a new stable release.</p><p>A <a href="https://www.drupal.org/project/issues/search?projects=&amp;project_issue_followers=&amp;issue_tags_op=%3D&amp;issue_tags=PHP+7.3">simple search on Drupal.org</a> yields the following modules that look like they may need work (more are certainly possible):</p><ul><li>composer_manager (<a href="https://www.drupal.org/project/composer_manager/issues/3058496">issue</a>)</li><li>scald (<a href="https://www.drupal.org/project/scald/issues/3032429">issue</a>) [now fixed and released]</li><li>video (<a href="https://www.drupal.org/project/video/issues/3042169">issue</a>)</li><li>search_api (<a href="https://www.drupal.org/project/search_api/issues/3009744">issue</a>) [now fixed and released]</li></ul><p>Most of the issues seem to be related to this deprecation - <a href="https://wiki.php.net/rfc/continue_on_switch_deprecation">Deprecate and remove continue targeting switch</a>. If you know of any other modules that have issues, please let me know in the comments.</p><h3 id="drupal-6">Drupal 6</h3><p>For all you die hard Drupal 6 fans out there (I know a few large websites still running this), you are going to be in for a rough ride. There is a <a href="https://github.com/d6lts/drupal/tree/php-7">PHP 7 branch of the d6lts Github repo</a>, so this is promising, however the last commit was September 2018, so this does not bode well for PHP 7.3 support. I also doubt contributed modules are going to be up to scratch (drupal.org does not even list D6 versions of modules anymore).</p><p>To test this theory, I audited the current 6.x-2.x branch of views</p><pre><code class="language-bash">$ phpcs -p ~/projects/views --standard=PHPCompatibility --runtime-set testVersion 7.3
................................................W.W.WW.W....  60 / 261 (23%)
................................E........................... 120 / 261 (46%)
...................................................EE....... 180 / 261 (69%)
............................................................ 240 / 261 (92%)
.....................                                        261 / 261 (100%)</code></pre><p>3 errors in views alone. The errors are show stoppers too</p><pre><code class="language-bash">Function split() is deprecated since PHP 5.3 and removed since PHP 7.0; Use preg_split() instead</code></pre><p>If this is the state of one of the most popular modules for Drupal 7, then I doubt the lesser known modules will be any better.</p><p>If you are serious about supporting Drupal 6, it would pay to get in contact with <a href="https://www.mydropwizard.com">My Drop Wizard</a>, as they <a href="https://www.mydropwizard.com/blog/drupal-6-year-2020-and-php-7-support">at least at providing support for people looking to adopt PHP 7</a>.</p>]]></content:encoded></item><item><title><![CDATA[Custom Cloudflare WAF rules that every Drupal site should run]]></title><description><![CDATA[This blog post helps to summarise some of the default rules I will deploy to every Drupal (7 or 8) site as a base line.]]></description><link>https://www.pixelite.co.nz/article/custom-cloudflare-waf-rules-that-every-drupal-site-should-run/</link><guid isPermaLink="false">60def49afc441200063df1d3</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Cloudflare]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[security]]></category><category><![CDATA[WAF]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Mon, 08 Jul 2019 16:00:00 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/07/gabriele-diwald-aL1Bp6Put2I-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/07/gabriele-diwald-aL1Bp6Put2I-unsplash.jpg" alt="Custom Cloudflare WAF rules that every Drupal site should run"><p>Part of my day job is to help tune the Cloudflare WAF for several customers. This blog post helps to summarise some of the default rules I will deploy to every Drupal (7 or 8) site as a base line.</p><p>The format of the custom WAF rules in this blog post are YAML format (for humans to read), if you do want to create these rules via the API, then you will need them in JSON format (see the end of this blog post for a sample API command).</p><h2 id="default-custom-waf-rules">Default custom WAF rules</h2><h3 id="unfriendly-drupal-7-urls">Unfriendly Drupal 7 URLs</h3><p>I often see bots trying to hit URLs like <code>/?q=node/add</code> and <code>/?q=user/register</code>. This is the default unfriendly URL to hit on Drupal 7 to see if user registration or someone has messed up the permissions table (and you can create content as an anonymous user). Needless to say, these requests are rubbish and add no value to your site, let&apos;s block them.</p><pre><code class="language-yaml">description: &apos;Drupal 7 Unfriendly URLs (bots)&apos;
action: block
filter:
  expression: &apos;(http.request.uri.query matches &quot;q=user/register&quot;) or (http.request.uri.query matches &quot;q=node/add&quot;)&apos;</code></pre><h3 id="autodiscover">Autodiscover</h3><p>If your organisation has bought <a href="https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/autodiscover-for-exchange">Microsoft Exchange</a>, then likely your site will receive loads of requests (GET and POST) to which is likely to just tie up resources on your application server serving these 404s. I am yet to meet anyone that actually serves back real responses from a Drupal site for Autodiscover URLs. Blocking is a win here.</p><pre><code class="language-yaml">description: Autodiscover
action: block
filter:
  expression: &apos;(http.request.uri.path matches &quot;/autodiscover\.xml$&quot;) or (http.request.uri.path matches &quot;/autodiscover\.src/&quot;)&apos;</code></pre><h3 id="wordpress">Wordpress</h3><p>Seeing as Wordpress has a huge market share (<a href="https://w3techs.com/technologies/details/cm-wordpress/all/all">34% of all websites</a>) a lot of Drupal sites get caught up in the mindless (and endless) crawling. These rules will effectively remove all of this traffic from your site.</p><pre><code class="language-yaml">description: &apos;Wordpress PHP scripts&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path matches &quot;/wp-.*\.php$&quot;)&apos;</code></pre><pre><code class="language-yaml">description: &apos;Wordpress common folders (excluding content)&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path matches &quot;/wp-(admin|includes|json)/&quot;)&apos;</code></pre><p>I separate <code>wp-content</code> into it&apos;s own rule as you may want to disable this rule if you are migrating from a old Wordpress site (and want to put in place redirects for instance).</p><pre><code class="language-yaml">description: &apos;Wordpress content folder&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path matches &quot;/wp-content/&quot;)&apos;</code></pre><h3 id="sqli">SQLi</h3><p>I have seen several instanced in the past where obvious SQLi was being attempted and the default WAF rules by Cloudflare were not intercepting them. This custom WAF rule is an attempt to fill in this gap.</p><pre><code class="language-yaml">description: &apos;SQLi in URL&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path contains &quot;select unhex&quot;) or (http.request.uri.path contains &quot;select name_const&quot;) or (http.request.uri.path contains &quot;unhex(hex(version()))&quot;) or (http.request.uri.path contains &quot;union select&quot;) or (http.request.uri.path contains &quot;select concat&quot;)&apos;</code></pre><h3 id="drupal-8-install-script">Drupal 8 install script</h3><p>Drupal 8&apos;s default install script will expose your major, minor and patch version of Drupal you are running. This is bad for a lot of reasons. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/07/drupal.png" class="kg-image" alt="Custom Cloudflare WAF rules that every Drupal site should run" loading="lazy"><figcaption>Drupal 8&apos;s default install screen exposes far too much information</figcaption></figure><p>It is better to just remove these requests from your Drupal site altogether. Note, this is not a replacement for upgrading Drupal, it is just to make fingerprinting a little harder.</p><pre><code class="language-yaml">description: &apos;Install script&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path eq &quot;/core/install.php&quot;)&apos;</code></pre><h3 id="microsoft-office-and-skype-for-business">Microsoft Office and Skype for Business</h3><p>Microsoft sure is good at making lots of products that attempt to DoS its own customers websites. These requests are always POST requests, often to your homepage, and you require partial string matching to match the user agent, as it changes with the version of Office/Skype you are running.</p><p>In large organisation, I have seen the number of requests here number in the hundreds of thousands per day.</p><pre><code class="language-yaml">description: &apos;Microsoft Office/Skype for Business POST requests&apos;
action: block
filter:
  expression: &apos;(http.request.method eq &quot;POST&quot;) and (http.user_agent matches &quot;Microsoft Office&quot; or http.user_agent matches &quot;Skype for Business&quot;)&apos;</code></pre><h3 id="microsoft-activesync">Microsoft ActiveSync</h3><p>Yet another Microsoft product that you don&apos;t why it is trying to hit another magic endpoint that doesn&apos;t exist.</p><pre><code class="language-yaml">description: &apos;Microsoft Active Sync&apos;
action: block
filter:
  expression: &apos;(http.request.uri.path eq &quot;/Microsoft-Server-ActiveSync&quot;)&apos;</code></pre><h2 id="using-the-cloudflare-api-to-import-custom-waf-rules">Using the Cloudflare API to import custom WAF rules</h2><p>It can be a pain to have to manually point and click a few hundred times per zone to import the above rules. Instead you would be better off to use the API. Here is a sample cURL command you can use do import all of the above rules in one easy go.</p><p>You will need to replace the redacted sections with your details.</p><pre><code class="language-bash">curl &apos;https://api.cloudflare.com/client/v4/zones/XXXXXXXXXXXXXX/firewall/rules&apos; \
  -H &apos;X-Auth-Email: XXXXXXXXXXXXXX&apos; \
  -H &apos;X-Auth-Key: XXXXXXXXXXXXXX&apos;
  -H &apos;Accept: application/json&apos; \
  -H &apos;Content-Type: application/json&apos;
  -H &apos;Accept-Encoding: gzip&apos;
  -X POST \
  -d &apos;[{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Autodiscover&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path matches \&quot;\/autodiscover\\.xml$\&quot;) or (http.request.uri.path matches \&quot;\/autodiscover\\.src\/\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Drupal 7 Unfriendly URLs (bots)&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.query matches \&quot;q=user\/register\&quot;) or (http.request.uri.query matches \&quot;q=node\/add\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Install script&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path eq \&quot;\/core\/install.php\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Microsoft Active Sync&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path eq \&quot;\/Microsoft-Server-ActiveSync\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Microsoft Office\/Skype for Business POST requests&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.method eq \&quot;POST\&quot;) and (http.user_agent matches \&quot;Microsoft Office\&quot; or http.user_agent matches \&quot;Skype for Business\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;SQLi in URL&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path contains \&quot;select unhex\&quot;) or (http.request.uri.path contains \&quot;select name_const\&quot;) or (http.request.uri.path contains \&quot;unhex(hex(version()))\&quot;) or (http.request.uri.path contains \&quot;union select\&quot;) or (http.request.uri.path contains \&quot;select concat\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Wordpress common folders (excluding content)&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path matches \&quot;\/wp-(admin|includes|json)\/\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Wordpress content folder&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path matches \&quot;\/wp-content\/\&quot;)&quot;}},{&quot;ref&quot;:&quot;&quot;,&quot;description&quot;:&quot;Wordpress PHP scripts&quot;,&quot;paused&quot;:false,&quot;action&quot;:&quot;block&quot;,&quot;priority&quot;:null,&quot;filter&quot;:{&quot;expression&quot;:&quot;(http.request.uri.path matches \&quot;\/wp-.*\\.php$\&quot;)&quot;}}]&apos;</code></pre><h2 id="how-do-you-know-the-above-rules-are-working">How do you know the above rules are working</h2><p>Visit the firewall overview tab in Cloudflare&apos;s UI to see how many requests are being intercepted by the above rules.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/07/firewall-1.png" class="kg-image" alt="Custom Cloudflare WAF rules that every Drupal site should run" loading="lazy"><figcaption>Cloudflare&apos;s firewall overview screen showing the custom WAF rules in action</figcaption></figure><h2 id="final-thoughts">Final thoughts</h2><p>The above custom WAF rules are likely not the only custom WAF rules you will need for any given Drupal site, but it should at least be a good start. Let me know in the comments if you have any custom WAF rules that you always deploy. I would be keen to update this blog post with additional rules from the community.</p><p>This is likely the first post in a series of blog posts on customising Cloudflare to suit your Drupal site. If you want to stay up to date - <a href="https://www.pixelite.co.nz/rss/">subscribe to the RSS feed</a>, <a href="https://www.pixelite.co.nz/#subscribe">sign up for email updates</a>, or <a href="https://twitter.com/pixelite_">follow us on Twitter</a>.</p>]]></content:encoded></item><item><title><![CDATA[JSON:API testing with Cypress]]></title><description><![CDATA[Upgrading JSON:API and Drupal core can be tricky to keep your API intact. Using Cypress is an easy way to have an extra set of eyeballs on the upgrade.]]></description><link>https://www.pixelite.co.nz/article/json-api-testing-with-cypress/</link><guid isPermaLink="false">60def49afc441200063df191</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Drupal planet]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Cypress]]></category><category><![CDATA[JSON:API]]></category><category><![CDATA[Entity]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Fri, 21 Jun 2019 01:00:33 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/06/cypress.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.pixelite.co.nz/content/images/2019/06/cypress.jpg" alt="JSON:API testing with Cypress"><p>I am working with a customer now that is looking to go through a JSON:API upgrade, from version 1.x on Drupal 8.6.x to 2.x and then ultimately to Drupal 8.7.x (where it is bundled into core).</p><p>As this upgrade will involve many moving parts, and it is critical to not break any existing integrations (e.g. mobile applications etc), having basic end-to-end tests over the API endpoints is essential.</p><p>In the past I have written <a href="https://www.pixelite.co.nz/tag/casperjs/">a lot about CasperJS</a>, and since then a number of more modern frameworks have emerged for end-to-end testing. For the last year or so, I have been involved with <a href="https://www.cypress.io/">Cypress</a>.</p><p>I won&apos;t go too much in depth about Cypress in this blog post (I will likely post more in the coming months), instead I want to focus specifically on JSON:API testing using Cypress.</p><p>In this basic test, I just wanted to hit some known valid endpoints, and ensure the response was roughly OK.</p><p>Rather than have to rinse and repeat a lot of boiler plate code for every API end point, I wrote a custom Cypress command, to which abstracts all of this away in a convenient function.</p><p>Below is what the <code>spec</code> file looks like (the test definition), it is very clean, and is mostly just the JSON:API paths.</p><figure class="kg-card kg-code-card"><pre><code class="language-JS">describe(&apos;JSON:API tests.&apos;, () =&gt; {

    it(&apos;Agents JSON:API tests.&apos;, () =&gt; {
        cy.expectValidJsonWithMinimumLength(&apos;/jsonapi/node/agent?_format=json&amp;include=field_agent_containers,field_agent_containers.field_cont_storage_conditions&amp;page[limit]=18&apos;, 6);
        cy.expectValidJsonWithMinimumLength(&apos;/jsonapi/node/agent?_format=json&amp;include=field_agent_containers,field_agent_containers.field_cont_storage_conditions&amp;page[limit]=18&amp;page[offset]=72&apos;, 0);
    });
    
    it(&apos;Episodes JSON:API tests.&apos;, () =&gt; {
        cy.expectValidJsonWithMinimumLength(&apos;/jsonapi/node/episode?fields[file--file]=uri,url&amp;filter[field_episode_podcast.nid][value]=4976&amp;include=field_episode_podcast,field_episode_audio,field_episode_audio.field_media_audio_file,field_episode_audio.thumbnail,field_image,field_image.image&apos;, 6);
    });

});</code></pre><figcaption>jsonapi.spec.js</figcaption></figure><p>And as for the custom function implementation, it is fairly straight forward. Basic tests are done like:</p><ul><li>Ensure the response is an HTTP 200</li><li>Ensure the content-type is valid for JSON:API</li><li>Ensure there is a response body and it is valid JSON</li><li>Enforce a minimum number of entities you expect to be returned</li><li>Check for certain properties in those returned entities. </li></ul><figure class="kg-card kg-code-card"><pre><code class="language-JS">Cypress.Commands.add(&apos;expectValidJsonWithMinimumLength&apos;, (url, length) =&gt; {
    return cy.request({
        method: &apos;GET&apos;,
        url: url,
        followRedirect: false,
        headers: {
            &apos;accept&apos;: &apos;application/json&apos;
        }
    })
    .then((response) =&gt; {
        // Parse JSON the body.
        let body = JSON.parse(response.body);
        expect(response.status).to.eq(200);
        expect(response.headers[&apos;content-type&apos;]).to.eq(&apos;application/vnd.api+json&apos;);
        cy.log(body);
        expect(response.body).to.not.be.null;
        expect(body.data).to.have.length.of.at.least(length);

        // Ensure certain properties are present.
        body.data.forEach(function (item) {
            expect(item).to.have.all.keys(&apos;type&apos;, &apos;id&apos;, &apos;attributes&apos;, &apos;relationships&apos;, &apos;links&apos;);
            [&apos;changed&apos;, &apos;created&apos;, &apos;default_langcode&apos;, &apos;langcode&apos;, &apos;moderation_state&apos;, &apos;nid&apos;, &apos;path&apos;, &apos;promote&apos;, &apos;revision_log&apos;, &apos;revision_timestamp&apos;, &apos;status&apos;, &apos;sticky&apos;, &apos;title&apos;, &apos;uuid&apos;, &apos;vid&apos;].forEach((key) =&gt; {
                expect(item[&apos;attributes&apos;]).to.have.property(key);
            });
        });
    });

});</code></pre><figcaption>commands.js</figcaption></figure><p>Some of the neat things in this function is that it does log the parsed JSON response with <code>cy.log(body);</code> this allows you to inspect the response in Chrome. This allows you to extend the test function rather easily to meet you own needs (as you can see the full entity properties and fields.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.pixelite.co.nz/content/images/2019/06/jsonapi-cypress.png" class="kg-image" alt="JSON:API testing with Cypress" loading="lazy"><figcaption>Cypress with a GUI can show you detailed log information</figcaption></figure><p>Using Cypress is like having an extra pair of eyes on the Drupal upgrade. Over time Cypress will end up saving us a lot of developer time (and therefore money). The tests will be in place forever, and so regressions can be spotted much sooner (ideally in local development) and therefore fixed much faster.</p><h2 id="comments">Comments</h2><p>If you do JSON:API testing with Cypress I would be keen to know if you have any tips and tricks.</p>]]></content:encoded></item><item><title><![CDATA[10 things I learnt building in Drupal 8]]></title><description><![CDATA[Some of the neat things I have found with Drupal 8 and some of my lessons learned]]></description><link>https://www.pixelite.co.nz/article/10-things-i-learnt-building-in-drupal-8/</link><guid isPermaLink="false">60def49afc441200063df18f</guid><category><![CDATA[Drupal]]></category><category><![CDATA[Twig]]></category><category><![CDATA[Drupal console]]></category><category><![CDATA[Drupal planet]]></category><dc:creator><![CDATA[Sean Hamlin]]></dc:creator><pubDate>Thu, 19 May 2016 03:24:00 GMT</pubDate><media:content url="https://www.pixelite.co.nz/content/images/2019/06/d8.png" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://www.pixelite.co.nz/content/images/2019/06/d8.png" alt="10 things I learnt building in Drupal 8"><p>I have had the chance to be involved with 2 fresh builds with Drupal 8 now, I thought I would describe some of the neat things I have found during this time and some of my lessons learned. My hope is that blog post will help you in your journey with Drupal 8.</p><h2 id="1-drupal-console-is-awesome">1. Drupal Console is awesome</h2><p>Every time you need to generate a custom module, or a new block in a custom module, you can quickly and easily use <a href="https://drupalconsole.com/">Drupal Console</a> to produce the code scaffolding for you. This quite easily makes the job of a developer a lot less stressful, and allows you to focus on actually writing code that delivers functionality.</p><p>I plucked these example commands that I use frequently from my bash history:</p><pre><code class="language-bash">drupal site:mode dev
drupal generate:module
drupal generate:plugin:block
drupal generate:routesubscriber
drupal generate:form:config</code></pre><p>Documentation is <a href="http://docs.drupalconsole.com/en/index.html">online</a> but for the most part, the commands are self documenting, if you use the <code>--help</code> option, then you get a great summary on the command, and the other options you can pass in.</p><p>The other nice thing is that this is a <a href="http://symfony.com/doc/current/components/console/introduction.html">Symfony Console</a> application, so it should feel very familiar to you if you used another tool written in the same framework.</p><h2 id="2-custom-block-types-are-amazing">2. Custom block types are amazing</h2><p>In Drupal 7 land there was <a href="https://www.drupal.org/project/bean">bean</a> which was an attempt to stop making &#x2018;meta&#x2019; nodes to fill in content editable parts of complex landing pages. Now, fast forward to Drupal 8, and custom block types are now in Drupal Core.</p><p>This basically means as a site builder you now have another really powerful tool at your disposal in order to model content effectively in Drupal 8.</p><p>Each custom block type can have it&#x2019;s own fields, it&#x2019;s own display settings, and form displays.</p><p>Here are the final custom block types on a recent Drupal 8 build:</p><figure class="kg-card kg-image-card"><img src="https://www.pixelite.co.nz/content/images/2019/06/blocktypes.png" class="kg-image" alt="10 things I learnt building in Drupal 8" loading="lazy"></figure><p>One downside is that there is no access control per custom block type (just a global permission &#x201C;administer blocks&#x201D;), no doubt contrib will step in to fill this hole in the future (does anyone know a module that can help here?). In the mean time there is <a href="https://www.drupal.org/node/1975064">drupal.org issue</a> on the subject.</p><p>I also found it weird that the custom blocks administration section was not directly under the &#x2018;structure&#x2019; section of the site, there is another <a href="https://www.drupal.org/node/2501691">drupal.org issue</a> about normalising this as well. Setting up some default shortcuts really helped me save some time.</p><figure class="kg-card kg-image-card"><img src="https://www.pixelite.co.nz/content/images/2019/06/shortcuts.png" class="kg-image" alt="10 things I learnt building in Drupal 8" loading="lazy"></figure><h2 id="3-view-modes-on-all-the-things">3. View modes on all the things</h2><p>To create custom view modes in Drupal 7 required either a custom module or Dave Reid&#x2019;s <a href="https://www.drupal.org/project/entity_view_mode">entity_view_mode</a> contrib module. Now this is baked into Drupal 8 core.</p><p>View modes on your custom block types takes things to yet another level still as well. This is one more feather in the Drupal site builder&#x2019;s cap.</p><h2 id="4-twig-is-the-best">4. Twig is the best</h2><p>In Drupal 7 I always found it weird that you could not unleash a front end developer upon your site and expect to have a pleasant result. In order to be successful the themer would need to know PHP, preprocess hooks, template naming standards, the mystical specific order in which the templates apply and so on. This often meant that a backend and front end developer would need to work together in order to create a good outcome.</p><p>With the introduction of Twig, I now feel that theming is back in the hands of the front end developer, and knowledge of PHP is no longer needed in order to override just about any markup that Drupal 8 produces.</p><p><strong><strong>Pro tip</strong></strong> - use the Drupal Console command <code>drupal site:mode dev</code> to enable Twig development options, and disable Drupal caching. Another positive side effect is that Twig will then render the entire list of templates that you could be using, and which one you actually are using (and where that template is located).</p><figure class="kg-card kg-image-card"><img src="https://www.pixelite.co.nz/content/images/2019/06/twigdevel.png" class="kg-image" alt="10 things I learnt building in Drupal 8" loading="lazy"></figure><p><strong><strong>Pro tip:</strong></strong> - If you want to use a template per custom block type (to which I did), then you can use this PHP snippet in your theme&#x2019;s <code>.theme</code> file (taken from <a href="https://www.drupal.org/node/2460893#comment-10766412">drupal.org</a>):</p><pre><code class="language-php">&lt;?php
/**
 * Implements hook_theme_suggestions_HOOK_alter() for form templates.
 *
 * @param array $suggestions
 * @param array $variables
 */
function THEMENAME_theme_suggestions_block_alter(array &amp;$suggestions, array $variables) {
  if (isset($variables[&apos;elements&apos;][&apos;content&apos;][&apos;#block_content&apos;])) {
    array_splice($suggestions, 1, 0, &apos;block__bundle__&apos; . $variables[&apos;elements&apos;][&apos;content&apos;][&apos;#block_content&apos;]-&gt;bundle());
  }
}</code></pre><h2 id="5-panelizer-panels-ipe-is-a-formidable-site-building-tool">5. Panelizer + panels IPE is a formidable site building tool</h2><p>When looking for a layout manager to help build the more complex landing pages, I came across <a href="https://www.drupal.org/project/panelizer">panelizer</a> + <a href="https://www.drupal.org/project/panels">panels IPE</a>. Using panelizer you are able to:</p><ul><li>create per node layout variants</li><li>apply a single layout to all nodes of a particular bundle (e.g. all your news articles have the same layout)</li></ul><p>The other neat thing is that the layouts themselves are now standardised between all the various layout managers using a contrib module called <a href="https://www.drupal.org/project/layout_plugin">layout_plugin</a>. Also they are just YAML and Twig. Simple. There is even an effort to get this <a href="https://www.drupal.org/node/2296423">merged into Drupal 8.2</a> which I think would be a great idea.</p><p><strong><strong>Downside</strong></strong> - all JS is still rendered on the page even though the user (e.g. anonymous users) have no access to panelizer. There is a patch on <a href="https://www.drupal.org/node/2688951">drupal.org</a> to help fix this.</p><p>Since starting this build there has also been a stable release of <a href="https://www.drupal.org/project/ds">display suite</a> come out for Drupal 8 as well giving you even more options.</p><h2 id="6-you-can-build-a-rather-complex-site-with-very-little-contributed-modules">6. You can build a rather complex site with very little contributed modules</h2><p>For this most recent site I build I got away with using only 10 contributed modules (one of which - <a href="https://www.drupal.org/project/devel">devel</a> was purely for debugging purposes).</p><ul><li>ctools</li><li>google_analytics</li><li>metatag</li><li>panels</li><li>token</li><li>contact_block</li><li>devel</li><li>layout_plugin</li><li>panelizer</li><li>pathauto</li></ul><p>This means you are inherently building a more stable and supportable site, as most of the functionality now comes out of Drupal core.</p><h2 id="7-the-contact-module-now-is-supercharged">7. The contact module now is supercharged</h2><p>In Drupal 7, the contact module was one of those modules to which I never turned on, as it was rather inflexible. You could not change the fields in a UI, nor add email recipients, or have more than 1 form. Now in Drupal 8 you can have as many &#x201C;contact&#x201D; forms as you want, each one is fieldable, and can send emails to as many people as needed.</p><p>You can also enhance the core module with:</p><ul><li><a href="https://www.drupal.org/project/contact_block">contact_block</a> - allows you to place the contact form in a block</li><li><a href="https://www.drupal.org/project/contact_storage">contact_storage</a> - allows you to store the submissions in the database, rather than firing an email and forgetting about it</li></ul><p>There is still a place for webform, namely:</p><ul><li>large complex form with lots of fields</li><li>multi-step forms</li><li>forms you want to &#x2018;save draft&#x2019;</li></ul><p>You can read more about this in the <a href="https://www.ostraining.com/blog/drupal/drupal-8-contact-forms/#comment-2333911795">OS training blog post</a> on the contact module.</p><p><strong><strong>Downside</strong></strong> - I wanted to have a plain page use the path <code>/contact</code> but the contact module registers this path, so pathauto gave my contact page a path of <code>/contact-0</code>. Luckily creating a <a href="https://www.drupal.org/node/2187643">route subscriber</a> with Drupal Console was painless, so altering the contact module route was very simple to do. I can paste the code here if needed, but most of it is the code that Drupal Console generates for you.</p><h2 id="8-phpunit-is-bundled-into-core">8. PHPunit is bundled into core</h2><p>Now that Drupal 8 is largely Object Oriented (OO), you are able to test classes using PHPunit. I have <a href="https://www.pixelite.co.nz/article/writing-phpunit-tests-for-your-custom-modules-in-drupal-8/">wrote about phpunit</a> in the past if you want to know more.</p><h2 id="9-views-is-in-core">9. Views is in core</h2><p>This was the main reason why adoption of Drupal 7 was so slow after it&#x2019;s initial 7.0 release, as everyone needed views to be stable before jumping ship. Now with views bundled into core, views plugins are also being ported at a great rate of knots too.</p><h2 id="10-ckeditor-is-in-core">10. CKEditor is in core</h2><p>I often found that this was one library that never (or hardly ever) got updated on sites that had been around for a while. More worryingly, CKEditor (the library) would from time to time fix security related issues. Now that this comes with Drupal 8 core, it is just one less thing to worry about.</p><p>Also I would love to shout out to <a href="https://www.drupal.org/u/wim-leers">Wim Leers</a> (and other contributors) for <a href="https://www.drupal.org/node/2027181">revamping the image dialog</a> with alignment and caption options. I cannot tell you how much pain and suffering this caused me in Drupal 7.</p><figure class="kg-card kg-image-card"><img src="https://www.pixelite.co.nz/content/images/2019/06/image.png" class="kg-image" alt="10 things I learnt building in Drupal 8" loading="lazy"></figure><h2 id="comments">Comments</h2><p>If you have built a site recently in Drupal 8 and have found anything interesting or exciting, please let me know in the comments. Also keen to see what sites people have built, so post a link to it if it is public.</p>]]></content:encoded></item></channel></rss>