Robotaxis drives miles just to get cleaned and charged; this new startup wants to fix that
Aseon Labs, which came out of Y Combinator's 2026 spring cohort, has raised $10 million from Crane Venture Partners and others.
找到 44 篇相关文章
Aseon Labs, which came out of Y Combinator's 2026 spring cohort, has raised $10 million from Crane Venture Partners and others.
Your product might rank on page one of Google and still be invisible to AI. When someone asks ChatGPT "what's the best project management tool for small teams," does your product show up? For most SaaS companies under 50 employees, the answer is no. At Inithouse, we built Be Recommended to answer that question with a number: a single AI visibility score from 0 to 100 that tells you exactly where you stand across four major AI engines. Here is how the scoring works under the hood. What the score measures The Be Recommended score captures how often, how prominently, and how positively AI engines mention your product when users ask category-relevant questions. A score of 0 means no AI engine mentions you at all. A score of 100 means every tested prompt across all four engines names your product as a top recommendation. The four engines we test against: ChatGPT (OpenAI), Perplexity , Claude (Anthropic), and Gemini (Google). Step 1: Prompt generation We start by building a bank of 50+ real prompts that a potential customer would actually type into an AI assistant. These are not keyword-stuffed test queries. They mirror how real people ask for recommendations. For a CRM product, that looks like: "What CRM should a 10-person startup use?" "Best alternatives to Salesforce for small businesses" "Compare CRM tools with good API integration" "Which CRM has the best free tier in 2026?" We group prompts into three categories: direct (user names the product category), comparative (user asks for alternatives or comparisons), and situational (user describes a problem without naming a category). Each category tests a different signal: brand recognition, competitive positioning, and contextual relevance. Step 2: Multi-engine querying Each prompt gets sent to all four AI engines through their APIs. We capture the full response text, not just a yes/no for whether your product appeared. The raw responses go into a structured analysis pipeline. We run queries from neutral accounts with n
Most on-page audits catch the obvious stuff: a missing title here, a duplicate meta description there. The thing that quietly costs you rich results is structured data that exists but is invalid, and most flat-list crawlers either skip it or bury it. Here is why it happens and how to catch it. The problem, concretely You add FAQ schema to a product page to win that expandable rich result in Google. You paste a JSON-LD block into the head, ship it, and move on. Six weeks later the rich result never showed up, and nobody knows why. The usual culprits are small and silent: A @type that does not match the content (FAQPage with no mainEntity ). A required property missing ( acceptedAnswer without text ). A trailing comma or a stray character that makes the JSON parse fail entirely. Schema that contradicts what is actually on the page, which Google can flag as spammy and ignore. None of these throw a visible error. The page renders fine. The schema is just dead weight, and a standard "issues" crawl that only counts titles and headings walks right past it. How to catch it First, validate the JSON itself. A block that does not parse is invisible to search engines. Even a quick local check surfaces the dumb-but-fatal errors: // Pull every JSON-LD block and check it parses + has a @type const blocks = [... document . querySelectorAll ( ' script[type="application/ld+json"] ' )]; blocks . forEach (( b , i ) => { try { const data = JSON . parse ( b . textContent ); if ( ! data [ " @type " ]) console . warn ( `Block ${ i } : missing @type` ); } catch ( e ) { console . error ( `Block ${ i } : invalid JSON ->` , e . message ); } }); If that logs an error, the schema was never going to work, no matter how perfect the markup looked. Second, check required properties for the specific type you are using. FAQPage needs mainEntity with Question items, each carrying an acceptedAnswer . Article needs headline , author , and datePublished . Validating "it parsed" is not the same as "it is c
Dein bestes Google-Ranking ist wertlos, wenn die Antwort schon vor dem Klick gegeben wurde. Genau das passiert gerade: Nutzer fragen ChatGPT, Claude oder Perplexity – und bekommen eine fertige Antwort mit drei, vier zitierten Quellen. Bist du nicht darunter, existierst du in diesem Moment nicht. Kein Ranking, kein Klick, keine zweite Chance. Die Disziplin, die das adressiert, heißt Generative Engine Optimization (GEO) . Und sie ist – anders als der Marketing-Lärm vermuten lässt – zu großen Teilen ein Engineering-Problem. Crawler-Zugang, Rendering, strukturierte Daten. Lauter Dinge, über die ein Entwickler entscheidet, nicht das Content-Team. SEO optimiert auf den Klick. GEO optimiert auf das Zitat. Der Unterschied ist nicht kosmetisch. Klassisches SEO will, dass du auf Platz eins rankst, damit jemand klickt. GEO will, dass ein Sprachmodell deinen Absatz wörtlich in seine Antwort übernimmt – inklusive Quellenangabe. Der Klick ist nur noch Bonus. Daraus folgt ein anderer Tech-Stack an Signalen: Aspekt Klassisches SEO GEO / KI-Sichtbarkeit Ziel Top-10 in Google Zitat in ChatGPT, Claude, Perplexity Relevante Bots Googlebot, Bingbot GPTBot, ClaudeBot, PerplexityBot Index-Hinweis sitemap.xml llms.txt + sitemap.xml Strukturierte Daten Rich Snippets Entity-Linking ( Organization , sameAs , @graph ) Rendering Google rendert JS (verzögert) viele KI-Bots rendern kein JS → SSR Pflicht Erfolgskontrolle Search Console, Rank-Tracker Citation- & Mention-Tracking in LLMs Die Hebel überschneiden sich – sauberes HTML, schnelle Antwortzeiten, valides Markup helfen beidem. Aber die Bots, die Index-Signale und die Erfolgskontrolle sind eigenständig. Wer GEO als „SEO mit neuem Namen" abtut, übersieht genau die Stellen, an denen es klemmt. Schritt 1: Lass die Bots überhaupt rein Bevor du über Content-Qualität nachdenkst, klär die banale Frage: Kommt der Crawler durch? Erstaunlich oft lautet die Antwort nein – und niemand merkt es, weil ein Browser die Seite ja problemlos lädt. Die drei Use
Your site ranks on Google. Your Core Web Vitals are clean. Your meta tags are in order. And yet, when someone asks ChatGPT, Perplexity, or Google's AI Overview a question your business should answer your content doesn't show up. Not because your SEO is broken. Because AI search engines don't work like Google. Google Reads Pages. AI Search Reads Passages. Google crawls your page, indexes it, and ranks it based on signals like backlinks, domain authority, and keyword relevance. The unit of ranking is the page. AI search engines ChatGPT, Perplexity, Claude, Gemini don't rank pages. They retrieve passages. They pull specific chunks of content that directly answer a query, synthesize a response, and surface it to the user often without the user ever clicking through to your site. If your content isn't structured to be retrieved at the passage level, it gets skipped entirely. The page might exist. The answer might be buried somewhere in a 1,500-word article. But if the AI can't extract it cleanly and confidently, it moves on to content that makes its job easier. That's the invisibility problem. And most websites have no idea it's happening to them. The Crawler Problem Nobody Is Talking About Before we even get to content structure, there's a more fundamental issue. AI search engines have their own crawl agents. OpenAI sends GPTBot. Anthropic sends ClaudeBot. Perplexity sends PerplexityBot. These bots need access to your site before any retrieval can happen and a significant number of websites are blocking them without realizing it. This happens in a few ways: Blanket disallow rules in robots.txt. Many sites, especially those built on managed platforms, use wildcard disallow rules that were written for a different era when the only crawler worth worrying about was Googlebot. Those same rules now block AI crawlers by default. Overly aggressive bot protection. Security tools and CDN configurations that flag unusual crawl patterns will sometimes block AI bots before they even
Author: Alex Isa (Webappski). This is the dev-tutorial cut of a longer piece on the Webappski blog — terminal-first, fewer words on the why. If a buyer asks ChatGPT "best CDN providers 2026" and your product is not in the answer, you lose the sale before you ever see the lead. The only honest way to know whether that is happening is to ask the engines the questions your buyers ask and read the raw answers — not trust a single dashboard score. Here is the loop we at Webappski run for a client, with the open-source tool aeo-platform (MIT, zero runtime deps). 1. Install and point it at the client's domain npm install -g aeo-platform cd client-audit && aeo-tracker init init writes a .aeo-tracker.json . The three things that matter: { "brand" : "Northwind CDN" , // illustrative, fictional brand "domain" : "northwind.example" , // registrable domain — subdomains count, spoof hosts don't "engines" : [ "openai" , "gemini" , "anthropic" ], // ChatGPT, Gemini, Claude "queries" : [ "best CDN providers 2026" , "best low-latency video streaming CDN 2026" , "alternatives to the market-leading CDN 2026" ] } The questions ARE the audit. A basket of vanity phrases produces a flattering, useless number; a basket of the buyer's real decision questions produces a number that predicts revenue. Freeze it, so next month's run is comparable. 2. Run it — sampled, not one noisy shot AI answers are non-deterministic: ask the same question twice and you can get a different list. A single pass turns that noise into a fake-precise number. So run each cell several times and let the score carry a confidence interval instead of pretending one shot is the truth: # plain single-shot run aeo-tracker run # sample each cell N times — the score comes back with a Wilson confidence interval aeo-tracker run --samples = 5 With --samples=5 , every (query × engine) cell is queried five times; the headline presence rate is then reported as a Wilson interval, and small samples are flagged as small rather than so
The Problem: How Website Slow Performance Costs Businesses Revenue A slow website doesn't just frustrate users it actively costs businesses money. Every additional second of load time causes visitors to leave, damages your search engine rankings and directly reduces conversions. Research shows that pages taking 5 seconds to load have a bounce rate 75% higher than pages loading in 1 second. For e-commerce sites, this translates to thousands of dollars in lost sales monthly. Recently, we worked with a client experiencing exactly this problem. Their website averaged 5-second load times, resulting in high bounce rates, poor mobile performance and declining search visibility. They needed immediate action. The Initial Audit: Identifying Website Speed Bottlenecks Before implementing solutions, we performed a comprehensive website audit using industry-standard tools like Google PageSpeed Insights, GTmetrix and WebPageTest. Step 1: Image Optimization – The Biggest Win Images typically account for 50-80% of page weight. Optimizing images delivered the most dramatic performance improvements. What to do: Converted to Modern Formats – We converted all PNG and JPEG files to WebP format, which provides 25-35% better compression than traditional formats while maintaining visual quality. Aggressive Compression – Images were compressed using lossless and lossy techniques without perceptible quality loss to users. Implemented Lazy Loading – Below-the-fold images were set to load only when users scrolled near them, not on initial page load. Responsive Images – Different image sizes were served based on device screen size, so mobile users didn't download desktop-sized images. Step 2: Minifying and Deferring Code – Eliminating Render-Blocking Resources JavaScript and CSS files were creating significant render-blocking bottlenecks. What to do: Minified CSS and JavaScript – Removed unnecessary characters (spaces, comments, line breaks) from all CSS and JavaScript files. Removed Unused Code
You update WordPress on a quiet afternoon — a routine release, the kind you've installed a hundred times. The dashboard says everything went fine. Then the 404s start: not a handful, but every long-headlined article in your archive, all at once, all in Arabic. Nothing in the update log mentions it. No plugin changed. And the cruel part: the data that made those URLs work is already gone — shaved off inside a database-upgrade routine that ran for a few milliseconds and reported success. This isn't a freak accident or a broken plugin. It's three separate assumptions baked into WordPress core, each hard-coding the same number — 200 — and Arabic sites are almost uniquely exposed to all three. We hit this running WordPress for Arabic newsrooms, the same high-traffic publishing we've written about surviving breaking-news spikes . We traced it to the exact lines in core and built a fix that survives every future update. Here's the whole story. Why Arabic URLs hit a wall English never does WordPress stores a post's slug in the post_name column of wp_posts , and a category or tag slug in the slug column of wp_terms . Both are VARCHAR(200) by default — room for 200 characters, which for an English headline is generous. "Everything you need to know about our new pricing" is barely fifty. You'd have to write a paragraph to run out. Arabic is a different arithmetic, because of what WordPress actually stores in that column. It doesn't keep the raw Arabic text — it stores the percent-encoded form, the same %XX sequence that travels in the URL. Take a single word: الذكاء → %d8%a7%d9%84%d8%b0%d9%83%d8%a7%d8%a1 Every Arabic letter is two bytes in UTF-8, and every byte becomes a three-character %XX token. So one Arabic character costs about six characters of column space. Do the division: VARCHAR(200) holds roughly 33 Arabic characters. A normal news headline — "القبض على المتهمين في قضية الاحتيال الإلكتروني" — blows past that before it's halfway done. So Arabic publishers learn early
Originally published on MRTD.NET — fast, sourced news on crypto security, cyber & SEO. The uncomfortable first lesson You built a clean site, submitted a sitemap, maybe pinged IndexNow — and Google still shows nothing. Here's the part most guides skip: getting indexed by Google and getting indexed by everything else are two different problems , and conflating them wastes weeks. We separate what actually moves Google in 2026 from the folklore that just feels productive. Bing, Yandex and ChatGPT are the easy half If you've set up IndexNow , you've largely solved discovery for Bing, Yandex, Naver, Seznam and Yep — you POST your new/changed URLs to one endpoint and they get notified instantly. And because ChatGPT Search retrieves from Bing's index , confirmed Bing indexing effectively gates your visibility in ChatGPT's web results. That's a big chunk of the modern search surface handled with one integration. The catch: Google does not use IndexNow. It has said so repeatedly. So every "instant indexing" claim that leans on IndexNow is talking about Bing's world, not Google's. For Google, you need different levers. What actually gets you into Google There are really only two fast paths, plus one slow one. 1. Google Search Console — the only direct lever. Verify your domain (a private DNS TXT record; it does not trigger penalties or "re-evaluation," a common fear), submit your sitemap.xml , then use URL Inspection → Request Indexing on your key pages. There's a soft daily cap (~10–12 URLs), so spread a new site's pages over a few days. GSC is also the only place you can see whether a domain carries an inherited problem — essential if you bought an aged or expired domain. 2. Links on pages Google already re-crawls hourly. Googlebot's crawl budget for a brand-new, zero-authority domain is tiny. The fastest way to get a new URL discovered is a link to it from a page Google visits constantly — Reddit, Hacker News, Medium, established communities. These links are usually nofoll
Local SEO rankings are not the same everywhere. Your website may rank in position 2 in Austin, position 8 in Dallas, and not appear at all in Chicago. That is why checking one generic Google result is not enough for local SEO. If you are working on local SEO , agency reporting, competitor monitoring, or location-based search analysis, you need to track rankings by city. A simple workflow looks like this: Keyword + city → SERP API → local search results → ranking check → CSV report In this tutorial, we’ll build a basic Python script that tracks local SEO rankings by city using a SERP API. We will: Define target keywords Define target cities Send city-specific searches to a SERP API Extract organic results Check where a target domain appears Save the ranking data to CSV This is not a full SEO platform, but it gives you the core logic behind many local rank tracking tools. Why city-level rankings matter Google search results are location-sensitive. A query like: best digital marketing agency may return different results in: New York Austin London Singapore Sydney This matters even more for local intent keywords, such as: dentist near me plumber in Chicago coffee shop in Austin real estate agent in Miami For these searches, the result page can include: organic results local packs Google Maps results ads business directories review sites service pages location-specific landing pages If you only check one location, you may miss what users actually see in other cities. For local SEO, ranking data without location context is incomplete. Why use a SERP API? You could try to check Google rankings manually. But that does not scale. You could also try to scrape Google directly, but that brings a lot of maintenance work: changing page layouts CAPTCHA blocked requests proxy handling inconsistent HTML location mismatch parser updates retry logic A SERP API gives you structured search results in JSON. Instead of parsing raw HTML, you get data like this: { "query" : "plumber in Aust
Your users have started asking ChatGPT and Perplexity instead of Google. So here is the uncomfortable question: when someone asks an AI engine "what is the best tool for <your category> ", does your product show up in the answer? Most founders have no idea. I didn't either, until I measured it — and the gap was nowhere near where I expected. So we built a CLI to measure it. It's called aeo-platform , it's MIT-licensed, it has zero runtime dependencies, and it runs entirely on your machine. This post is the five-minute version: install it, point it at your domain, and read the gap. I'll show you the exact commands and the real before/after numbers from running it on one of our own products. Quick framing on terms: AEO (answer engine optimization) is just SEO's younger sibling for AI answers — getting cited inside the AI's response instead of ranking on a SERP. Some people call it GEO. Same field. TL;DR — three commands npm install -g aeo-platform export OPENAI_API_KEY = "sk-proj-..." # required export GEMINI_API_KEY = "AIzaSy..." # required aeo-platform init --yes --brand = YOURBRAND --domain = YOURDOMAIN.COM --auto \ && aeo-platform run \ && aeo-platform report init auto-discovers your category and writes three commercial buyer queries to a local .aeo-tracker.json . run fires those queries at each engine and scores the answers. report opens a single-file HTML report in your browser. The whole thing installs in under a second (no dependency tree to resolve) and writes everything to disk under aeo-responses/YYYY-MM-DD/ — nothing is sent to a hosted dashboard. OpenAI and Gemini keys are mandatory (they also power a two-model cross-check that filters hallucinated brand mentions). Anthropic and Perplexity keys are optional — each one just adds a column to the report. What it actually measures A single run sends your buyer queries to four engines through their official REST APIs — no scraping, no proprietary black-box score: Engine Model Type ChatGPT (OpenAI) gpt-5-search
Last weekend I started building SiteGrade — a free instant SEO audit tool that gives any website an A–F letter grade and the top fixes ranked by impact. I just launched it. This post is the build log: the architecture decisions that worked, the ones that didn't, and the gotchas you'd save time knowing about. If you're considering building a similar audit/scanner tool, or just want to see what a 2026 Next.js + Supabase + Stripe stack looks like in practice, this should be useful. Why I built it I got tired of family and friends asking me "how's the SEO on my website?" and not having a good answer to send back. Every existing tool I tried either cost $99+/month (Ahrefs, Semrush) or threw 200 metrics at people who just wanted to know if their site was OK. The wedge I built around: one letter grade, three top fixes, plain English, no signup. The paid tier ($29/mo) re-audits weekly and emails the report so non-technical users can track improvements as their developer makes them. The stack Nothing exotic. Everything is the obvious choice for a 2026 indie SaaS, which is the whole point — boring stack means I spent zero time fighting infrastructure and 100% of my time on the actual audit logic. Next.js 14 App Router — frontend + API routes in one repo Supabase — Postgres + auth + RLS + file storage, EU-hosted for GDPR Stripe — subscriptions + Billing Portal + webhooks Resend — transactional email (audit reports, weekly reports, Supabase auth via Custom SMTP) Vercel — hosting + cron jobs for the weekly re-audit cheerio — HTML parsing for the audit checks Google PageSpeed Insights API — Core Web Vitals + mobile performance Total monthly infrastructure cost at zero traffic: ~$0 (everyone on free tiers). At 100 paying customers it'd still be under $30/mo. The audit logic — what 15 checks look like in code Each audit runs 15 checks and produces a score from 0-100, then maps that to a letter grade (A: 90+, B: 75+, C: 60+, D: 45+, F: below 45). The checks are structured as pure fu
We run 10+ web products. For years our SEO tooling was a $100+/month subscription we mostly used for one job: technical audits. The keyword and backlink dashboards sat untouched while the bill renewed every month. At some point the math stopped making sense, so we rebuilt our stack around what we actually use. Here's the honest breakdown — what we kept free, what we replaced, and why. What a technical audit actually needs to check Before picking tools, it helps to know what you're auditing. The technical layer is finite and well-defined: Crawlability — robots.txt, broken internal links, redirect chains Indexability — noindex tags, canonical correctness, soft 404s, orphan pages Sitemaps — only canonical, indexable, 200-status URLs On-page — titles, meta descriptions, one H1, valid structured data International — hreflang reciprocity (if you're multilingual) Performance — Core Web Vitals (LCP, CLS, INP) That's it. None of it requires a keyword database or a backlink index — the two things you're really paying $100+/mo for in the big suites. The free tools that cover most of it Google Search Console is non-negotiable and free. The Pages report is the source of truth for what's indexed and why the rest isn't. PageSpeed Insights (free) gives you real Core Web Vitals. Screaming Frog's free tier crawls up to 500 URLs, which is plenty for small sites. For a long time that was our whole stack: GSC + PageSpeed + free Screaming Frog. If your site is under 500 pages, you may not need anything else. Where it broke down for us The free Screaming Frog cap (500 URLs) is the wall. Several of our sites are bigger, and re-auditing them meant either the paid Screaming Frog licence (annual) or going back to a monthly suite. Both felt wrong for something we run after every deploy. So we built our own crawler for the technical layer — and then, honestly, turned it into a product because other people we talked to had the same problem. It does the whole checklist above across the entire sit
Search engine optimization doesn't require expensive tools. Your browser's built-in developer tools can identify and diagnose most SEO problems in under 15 minutes. Here are the 11 most impactful SEO checks you can run directly from Chrome DevTools. 1. Check Meta Description Length Right-click any element, Inspect, expand <head> , find <meta name="description"> . Metric Optimal Range Meta description length 120-155 characters Title tag length 50-60 characters H1 count per page Exactly 1 const meta = document . querySelector ( ' meta[name="description"] ' ); const len = meta ? meta . content . length : 0 ; console . log ( `Meta description: ${ len } chars` ); 2. Verify Only One H1 Tag Exists const h1s = document . querySelectorAll ( ' h1 ' ); console . log ( `H1 count: ${ h1s . length } ` ); A study of 1.2 million pages found pages with one H1 ranked 12% higher on average. 3. Find Images Missing Alt Text const imgs = document . querySelectorAll ( ' img ' ); const missing = [... imgs ]. filter ( i => ! i . alt || i . alt . trim () === '' ); console . log ( ` ${ missing . length } of ${ imgs . length } images missing alt text` ); Pages with complete alt text see 3.7% higher image search visibility. 4. Detect Render-Blocking Resources Open DevTools, Network tab, reload, click "Blocking" filter. Resources in red block first contentful paint. Total blocking time under 200ms More than 20 render-blocking resources signals a problem Each render-blocking CSS file adds 50-300ms to page load 5. Check Canonical Tag Consistency const canonical = document . querySelector ( ' link[rel="canonical"] ' ); console . log ( canonical ? `Canonical: ${ canonical . href } ` : ' No canonical tag ' ); 6. Audit Internal Links const links = [... document . querySelectorAll ( ' a[href] ' )]; const internal = links . filter ( l => { try { return new URL ( l . href ). hostname === window . location . hostname ; } catch ( e ) { return false ; } }); console . log ( `Internal links: ${ internal . len
Setting up a 10-page marketing site Count the tools. You reach for Next.js or Astro. You run npm init . There's a bundler config. PostCSS for Tailwind. A Netlify account. A GitHub repository. Environment variables for the CMS token. A build hook URL from Netlify, pasted into the CMS webhook settings so content publishes trigger a rebuild. Maybe a CI configuration file. For a 10-page marketing site. The honest question: does a 10-page marketing site need any of this? For most content-focused sites, the answer is no. How we got here The JAMstack movement was right about the fundamentals. Pre-generated static HTML served from a CDN is faster, cheaper, and more secure than server-side rendering at request time. Netlify and Vercel made this genuinely accessible. The developer tooling that emerged around it was designed well. The problem is that the tooling was designed for applications — large JavaScript codebases with complex component trees, client-side routing, and sophisticated build requirements. It's good tooling for that use case. It became the default for everything, including content sites that have no more complexity than a Markdown file and a CSS stylesheet. When the tool doesn't fit the problem, you inherit the cost of the tool without the benefit. What the build pipeline actually costs Setup time. Getting a new project from blank to deployed with a modern build pipeline takes a few hours if you know the stack, longer if you're making decisions. Framework version, bundler config, PostCSS, Tailwind setup, environment variable management, deploy configuration. That's before you've written a single line of page-specific code. Maintenance surface. Every dependency is something that can break. Node version compatibility, framework major version upgrades, bundler updates, plugin compatibility matrices. A project set up in 2022 may require non-trivial work to update in 2026. For a client site, that maintenance either falls on your agency or accumulates as technical
I run a couple of small niche sites — a pet-health tool and a travel-info site. The thing that actually moves the needle for both isn't clever copywriting. It's having a lot of pages that each answer one specific, low-competition question. "Puppy vaccine schedule by breed." "Is [neighborhood] worth visiting." That kind of thing. This approach has a name: programmatic SEO . You take a data set (one row = one page), pour it into a template, and generate pages at scale. Done well, it's how directories, comparison sites, and tool sites quietly rank for thousands of long-tail terms. Done badly, it's a spam farm that Google buries. More on that later, because it matters. The problem: the tooling is either expensive or a Rube Goldberg machine When I went looking for a way to do this without hand-coding every page, I found two camps: Agency-grade SaaS — powerful, but priced at $99–$299/month . That's a lot of money to spit out HTML when you're a solo operator running sites that make beer money. No-code stacks — wire a spreadsheet to a CMS to a static-site generator with a couple of automation tools in between. It works, but now you maintain a fragile chain of four services, and your pages live inside someone else's platform. Neither felt right. I just wanted: spreadsheet in, clean HTML out, files I own. What I actually built (for myself, first) So I wrote a generator for my own sites. Every morning it reads a CSV, applies a template, and produces a folder of static HTML pages — each with valid JSON-LD, proper meta tags, an internal-link hub, and a sitemap. I deploy the folder. Done. After running it daily for months on my own properties, I cleaned it up and turned it into a product: PageForge . The core idea is deliberately boring: CSV + template → a ZIP of clean static HTML pages you own. No dashboard you have to log into forever. No lock-in. The output is just files. If you stop using PageForge tomorrow, your pages keep working because they're plain HTML sitting in your r
I've been building a static Astro site on Cloudflare Pages over the last few weeks. Sharing the 3 deployment bugs that cost me the most time, in case they save anyone else the same loop. Setup Astro 5 + Cloudflare Pages + Tailwind 4. Content lives in a few JSON files; each page is a dynamic route mapped over the data. Free-tier hosting, no backend. Standard static-first stack. Bug 1: Trailing-slash 307 chain I started with trailingSlash: 'never' in Astro config. Build output went to dist/foo/index.html . Result: Astro emitted canonical tags as /foo (no slash), but Cloudflare Pages served /foo/ (auto-adding the slash via 307). Google Search Console flagged pages as "Redirect error" because the canonical URL pointed at a redirect chain instead of a real 200. I first tried build.format: 'file' to get flat dist/foo.html output, hoping that would bypass the trailing slash. That made it worse — Cloudflare still 307-stripped, but now to a non-existent .html file → 404. Fix: stop fighting the platform. js // astro.config.mjs export default defineConfig({ trailingSlash: 'always', // ... }); trailingSlash: 'always' plus default directory build aligns the canonical URL with what Pages actually serves. The redirect errors resolved on next re-crawl. Bug 2: _redirects rejected at deploy I tried to do a www → apex 301 in public/_redirects : https://www.example.com/* https://example.com/:splat 301! Cloudflare rejected the deploy with three validation errors: Line 13: Only relative URLs are allowed. Line 22: Duplicate rule for path /foo. Line 23: Duplicate rule for path /bar. Pages tightened _redirects validation — absolute-URL sources aren't accepted anymore. The duplicate errors were because Astro's own redirects config in astro.config.mjs generates HTML meta-refresh files that Pages parses as implicit redirect rules — conflicting with my explicit ones. Fix: delete _redirects entirely. Use a Cloudflare Redirect Rule from the dashboard for cross-host 301s (Wildcard pattern,
Big update. The PDF hub is fully built out and a whole set of Office conversion tools just shipped. PDF hub complete (18 tools): Merge, split, compress, rotate, reorder, extract pages, remove pages, watermark, page numbers, protect, unlock, extract images, and more. Office ↔ PDF (6 tools): Word to PDF Excel to PDF PowerPoint to PDF PDF to Word PDF to Excel PDF to PowerPoint Office ↔ Office (3 tools, fully browser-side, no file upload): Word to Excel Excel to Word PowerPoint to Word Other recent updates: WordPress plugin updated to include the new PDF tools Chrome extension v1.1.0 updated with PDF support 1.6K pages now indexed on Google across 25 languages That brings the site to 61 free tools across three hubs (Image, PDF, Office), no signup required.
I'll be honest, when I first heard about Astro, I was skeptical. Another JavaScript framework? I already had React, Next.js was doing fine. Why bother? Then I actually used it. I built the website for Zimnovate, my AI-native digital product studio based in Harare, Zimbabwe, with Astro, ran it through Google PageSpeed Insights, and got scores I'd never seen before on a site I actually built myself. That changed everything for me. Let me tell you what Astro is, why it's architecturally different, and why it's worth learning, especially if you care about performance and SEO. What Even Is Astro? Astro is a web framework built around one radical idea: ship zero JavaScript by default . Most modern frameworks (React, Vue, Svelte) are component-based and hydrate the entire page on the client. Even if your page is mostly static content, the user's browser still downloads and runs JavaScript to render it. Astro flips this. It renders your components to pure HTML at build time. JavaScript only runs in the browser when you explicitly need it, and only for the specific components that need it. This isn't just a config option. It's the core architecture. The Architecture: Islands Astro uses a pattern called Islands Architecture . Think of your page as a static ocean with interactive "islands" floating in it. The ocean (static content, headings, text, images) ships as plain HTML. The islands (a navbar with a dropdown, a contact form, a live counter) are the only parts that hydrate with JavaScript. --- // This runs only at build time zero runtime cost const services = await fetch('/api/services').then(r => r.json()) --- <html> <body> <!-- Pure static HTML, no JS needed --> <h1>Zimnovate</h1> {services.map(service => <ServiceCard service={service} />)} <!-- This island hydrates only when visible --> <ContactForm client:visible /> </body> </html> The client:visible directive tells Astro: "only load this component's JavaScript when it scrolls into the viewport." You get full interacti
When I decided to ship this blog in four languages, I had a clear mental ranking. English would win on volume. Spanish would be runner-up because of the sheer speaker count. Japanese would stay steady because it's my native language. Portuguese, I figured, was the long tail. I added it mostly out of completism. Twenty-two days later, the GA4 snapshot disagrees with every part of that ranking. PT: 748 pageviews , 709 sessions EN: 195 pageviews , 176 sessions JA: 27 pageviews , 29 sessions ES: 7 pageviews , 7 sessions That is Portuguese pulling roughly 3.8× English, 28× Japanese, and 107× Spanish on the same blog, same publishing cadence, same author. One Portuguese article on its own (a post about a 24-hour security agent: 375 PV) got more pageviews than my entire English blog combined. I wrote that article hoping Spanish would surprise me. Instead Portuguese surprised me, and Spanish quietly continued to not exist. The setup, so you can discount my numbers properly This is not a clean comparative experiment. It's a single blog, kenimoto.dev , running four language directories ( /en/ , /ja/ , /pt/ , /es/ ). Articles get translated through a cross-language LLM pipeline, then hand-edited for register and locale (BR Portuguese vs PT Portuguese, LatAm-neutral Spanish vs Spain Spanish). The window: 2026-04-30 to 2026-05-21, 22 daily snapshots. EN has 26 articles. JA has 25. PT has 17. ES has 10. So PT has fewer articles than EN and still beats it almost 4 to 1. If you stop reading here, take this one thing: language asymmetry can swallow article-count asymmetry whole . Adding articles in a saturated language is slower than adding articles in an underserved one. Why Portuguese pulled ahead I don't think the answer is "Portuguese readers like me more." I think three asymmetries are stacking on top of each other. 1. TabNews is a community door English doesn't have TabNews is a Brazilian developer community where you can post a technical article and have it actually read by h