In-Page SEO Audit

AstraRad · astrarad.com

8 public marketing routes audited against 84 applicable checks across 18 categories. Scored against what a crawler actually receives from the live Netlify deployment.

Baseline — 2026-07-31 Angular 19 SPA Client-rendered only Netlify 8 pages
48%
Poor
40 / 84 checks

Per-Page Results

Scored over the 12 page-scoped categories. Site-wide checks (robots, sitemap, analytics, social, DNS, llms.txt) are scored once in the panel below.

RouteScoreStatus MetaOGTwitterJSON-LDHeadingsImages WordsCritical
/ 55%Fair 1,1414
/services 53%Fair 8364
/how-we-work 53%Fair 1,1044
/our-radiologists 55%Fair 7044
/pricing 51%Fair 6305
/about 55%Fair 7084
/for-radiologists 55%Fair 5804
/contact 52%Fair 1955
Pass Partial Fail Not applicable Word counts are post-JavaScript; a non-JS crawler sees 0 on every route.

Site-Wide Configuration

Infrastructure and domain-level signals, checked once against the live deployment and DNS.

Canonical domain
https://astrarad.com
Resolves and serves over HTTP/2. But no page declares a canonical link, so the canonical host is never asserted to crawlers.
robots.txt
Missing (returns HTML)
/robots.txt returns HTTP 200 with the SPA shell. Crawlers parse HTML as robots directives and get nothing usable.
sitemap.xml
Missing — 0 of 8 pages listed
Same SPA-fallback problem. No sitemap has ever been submitted.
llms.txt
Missing (returns HTML)
No machine-readable summary for LLM crawlers (Perplexity, ChatGPT search, Claude).
Default OG image
None
No 1200x630 share asset exists anywhere in frontend/public/. Links pasted into Slack, LinkedIn or email render as bare text.
Organization schema
Missing
Zero application/ld+json blocks site-wide. No Organization, MedicalBusiness, WebSite, Service, FAQPage or BreadcrumbList.
HTTPS redirect
Working (301)
HTTP redirects to HTTPS. HSTS present, max-age 31536000.
Protocol & favicon
HTTP/2 · favicon OK
Both favicon.svg and favicon.ico return 200.
Search Console
Verified via DNS
TXT record google-site-verification=6XJWVOqHL_73fhFX… is live. Property is verified but has no sitemap to submit yet.
Analytics
None installed
No GA4, no GTM, no Meta Pixel. There is currently no way to measure whether any SEO work moves traffic.
Email DNS
SPF + DMARC present
SPF v=spf1 include:_spf.google.com ~all; DMARC p=quarantine with an rua address.
Social profiles
0 linked
No LinkedIn, X or Facebook link in the footer or anywhere on the site.

Category Score Breakdown

A category check passes only when it passes on every applicable page. 40 of 84 applicable checks pass.

Internal Linking
3/3 · 100%
Performance / CWV
8/9 · 89%
Accessibility-SEO
3/4 · 75%
Email & Security
3/4 · 75%
Mobile Friendliness
3/4 · 75%
Heading Hierarchy
3/5 · 60%
Image SEO
3/5 · 60%
Technical SEO
4/7 · 57%
Meta Tags
4/8 · 50%
Content Quality
2/4 · 50%
Monitoring & Analytics
2/4 · 50%
Content Analysis
2/4 · 50%
Open Graph
0/6 · 0%
Twitter Cards
0/4 · 0%
Structured Data
0/5 · 0%
Social Presence
0/3 · 0%
LLM Readability
0/3 · 0%
Local Business
0/2 · 0%

Issues by Priority

26 distinct findings. Counts in parentheses are the number of failing checks folded into each group.

Critical 6 findings
site-wide
No server-rendered content on any route. All 8 URLs return the same 8,748-byte shell with an empty <app-root>. Non-JS crawlers and most LLM crawlers index nothing. This single defect is what caps every content, heading and keyword category below.
Technical SEO
/robots.txt
robots.txt does not exist. The Netlify SPA catch-all returns HTTP 200 with HTML. Crawlers receive an unparseable robots file rather than a clean 404.
Technical SEO
/sitemap.xml
sitemap.xml does not exist — 0 of 8 public routes are discoverable via sitemap. Search Console is verified but has nothing to submit.
Technical SEO
all 8 pages
No canonical URL. Zero <link rel="canonical"> anywhere. With an SPA that serves identical HTML on every path, this is an active duplicate-content risk.
Meta Tags
all 8 pages
One meta description reused on all 8 pages, and it is 218 characters (limit 160, Semrush target 100-130). Google will truncate it, and 7 pages have no description of their own content at all.
Meta Tags
all 8 pages
Title tags are wrong in both directions. The served HTML gives all 8 routes the same 61-character title. After JavaScript, per-route titles are 16-38 characters — every one below the 50-60 target, and none carry a differentiating keyword beyond the page name.
Meta Tags
High 8 findings
all 8 pages
Zero Open Graph tags — 48 missing tags site-wide (og:title, og:description, og:image, og:type, og:url, og:image dimensions). Links shared into LinkedIn, Slack or email render as naked URLs, which matters for a B2B sales motion driven by outbound.
Open Graph
all 8 pages
Zero Twitter Card tags — 32 missing tags site-wide.
Twitter Cards
site-wide
No og:image asset exists. There is no 1200x630 share image in frontend/public/, so Open Graph cannot be completed without producing one first.
Open Graph
all 8 pages
No structured data of any kind. No Organization or MedicalBusiness, no WebSite, no Service, no FAQPage, no BreadcrumbList. No eligibility for rich results, and nothing for AI answer engines to attribute.
Structured Data
site-wide
No analytics installed. No GA4, no GTM, no pixel. Every subsequent SEO change will be unmeasurable until this lands.
Analytics
all 8 pages
Heading levels skip. The footer emits H4 headings directly after an H2 on every page. /contact is worst: H1 jumps straight to H4 with no H2 or H3 in between.
Headings
4 pages
H1 contains no primary keyword on /services ("Every modality. Every hour. Any volume."), /how-we-work, /pricing and /contact. Strong marketing copy, but it gives search engines no topical signal.
Headings
all 8 pages
Tap targets below 48px. Between 20 and 32 interactive elements per page (nav links, footer links, table CTAs) fall under the 48x48 CSS-pixel minimum. Google indexes mobile-first.
Mobile
Medium 12 findings
all 8 pages
Text-to-code ratio is 4.1% to 17.1%, against a 25% target. Driven by the inlined critical CSS in the shell plus Tailwind utility classes on every element.
Content Analysis
all 8 pages
"Teleradiology" appears at 0.09%-0.26% density — one to three mentions per page, against a healthy 1-3% band. The site's actual keyword weight sits on "subspecialist" (2.5% on the home page) and "radiologist" (1.4-2.2%). The primary commercial term is effectively absent.
Content Analysis
6 images
No width or height attributes on any image across /, /how-we-work and /our-radiologists. Unreserved layout space is a direct CLS risk.
Image SEO
/our-radiologists
No WebP anywhere; cxr.png is 484KB. All 6 images are PNG or JPEG. The scan assets total 1.5MB and push the home page to a 1.83MB transfer.
Image SEO
/llms.txt
No llms.txt. Combined with the client-only rendering, AI answer engines have no reliable way to read or cite AstraRad.
LLM Readability
site-wide
No social profile links. LinkedIn, X and Facebook are all unlinked from the footer. For a clinical B2B vendor, an unlinked LinkedIn presence is a trust gap as much as an SEO one.
Social
all 8 pages
No skip-to-content link before the navigation.
Accessibility
/contact
Thin content: 195 words, below the 300-word floor. It is the site's primary conversion page and the one most likely to be searched by brand name.
Content Quality
/pricing
No primary keyword in the first 100 words of body copy. The opening reads as pure commercial terms with no topical anchor.
Content Quality
site-wide
No phone number and no physical address on any page, including /contact. Healthcare buyers expect a callable number, and both are prerequisites for any location or MedicalBusiness schema.
Local Business
/ and 7 others
Inline styles present — 26 styled elements on the home page, 3 on each other page.
Email & Security
site-wide
INP could not be verified. The PageSpeed Insights API returned HTTP 429 (daily quota exhausted) at audit time. Lab signals are healthy — no console errors, DOM under 554 nodes, transfer under 1.9MB — but field INP remains unmeasured and is scored as not-passed.
Performance

What Already Passes

Worth stating plainly, because the delivery pipeline is in better shape than the score suggests.

Performance
Critical CSS is inlined by Beasties, the main stylesheet loads non-blocking via media="print" with an onload swap, fonts declare font-display:swap, and all bundles are minified. Zero console errors on all 8 routes. DOM sizes are 187-554 nodes against a 1,500 ceiling.
8/9
Internal linking
Every public route is reachable from both the nav and the footer, no broken internal links, and not a single "click here" or "read more" anchor anywhere on the site.
3/3
Semantics & forms
nav, main, header, footer and section are used correctly throughout, lang="en" is set, and all 13 inputs on the contact form have associated labels.
A11y
Image alt text
All 6 images have descriptive alt text written for the clinical context ("Axial CT slice arriving in the reading queue", "FDG PET brain study on a reading workstation") — not filenames.
Image SEO
Content
7 of 8 pages clear 300 words with genuinely unique, specific copy — no boilerplate duplication between pages. No deprecated tags, no untrusted iframes, no plain-text email addresses exposed to scrapers.
Content
Infrastructure
HTTP/2, HSTS, working 301 to HTTPS, valid favicon, SPF and DMARC (p=quarantine) both configured, and Search Console already verified by DNS TXT record.
Technical

Action Plan

Every failed check converted to a specific task, ordered Critical then High then Medium.

0 of 44 completed
Critical 14 items
site-wide Enable Angular prerendering so all 8 marketing routes ship real HTML, then confirm each route returns a distinct byte length (not the shared 8,748-byte shell) Technical SEO
robots.txt Add frontend/public/robots.txt with Allow rules and a Sitemap: https://astrarad.com/sitemap.xml line, and exclude it from the Netlify SPA catch-all so it stops returning HTML Technical SEO
sitemap.xml Generate sitemap.xml listing all 8 public routes as absolute HTTPS URLs with lastmod, and exclude it from the SPA catch-all Technical SEO
netlify.toml Add explicit static-file rules above the /* SPA fallback so robots.txt, sitemap.xml and llms.txt are served as files rather than swallowed Technical SEO
all 8 pages Add a per-route absolute canonical link (https://astrarad.com/<route>) emitted into prerendered HTML, not set client-side only Meta Tags
/ Write a unique 120-155 char meta description covering 600,000 signed reads a year, 28-minute median STAT turnaround and 24/7/365 subspecialist coverage (current shared one is 218 chars) Meta Tags
/services Write a unique 120-155 char meta description covering emergency reads, routine volume, subspecialist reporting and second opinions across twelve modalities Meta Tags
/how-we-work Write a unique 120-155 char meta description covering DICOM in from any PACS, HL7 results back to RIS and EMR, and the timed path to a signed report Meta Tags
/our-radiologists Write a unique 120-155 char meta description covering 240 board-certified, fellowship-trained subspecialists across ten subspecialties and twelve time zones Meta Tags
/pricing Write a unique 120-155 char meta description covering per-signed-report pricing by modality and complexity tier, no platform fees and no minimums Meta Tags
/about Write a unique 120-155 char meta description covering clinician operators who staffed and ran hospital radiology departments Meta Tags
/for-radiologists Write a unique 120-155 char meta description covering subspecialty-matched worklists, payout terms and flexible reading schedules Meta Tags
/contact Write a unique 120-155 char meta description covering how to request a per-report quote and coverage plan, with the stated response window Meta Tags
all 8 pages Rewrite all 8 route titles to 50-60 chars with a differentiating keyword each, and emit them into prerendered HTML (currently 16-38 chars client-side, one shared 61-char title in the served HTML) Meta Tags
High 15 items
public/ Produce a 1200x630 branded og-image.png (AstraRad wordmark plus the 600,000-reads proof point) — required before any og:image tag can be added Open Graph
all 8 pages Add the full Open Graph set per route: og:title, og:description, og:type=website, og:url (absolute), og:image, og:image:width=1200, og:image:height=630 Open Graph
all 8 pages Add twitter:card=summary_large_image plus twitter:title, twitter:description and twitter:image per route Twitter Cards
/ Add Organization (or MedicalBusiness) JSON-LD with name, url, logo, description and sameAs social profiles Structured Data
/ Add WebSite JSON-LD with url and name in its own script block Structured Data
/services Add Service JSON-LD covering emergency reads, routine volume, subspecialist reporting and second opinions, with provider pointing at the Organization Structured Data
/how-we-work Add HowTo JSON-LD for the PACS-to-signed-report sequence, using the existing timed steps Structured Data
/pricing Add Offer or PriceSpecification JSON-LD for the per-report tiers Structured Data
7 inner pages Add BreadcrumbList JSON-LD to every route except the home page Structured Data
all 8 pages Install GA4 with a real measurement ID loaded async, firing an SPA route-change pageview (needs a credential — see Your Next Steps) Analytics
all 8 pages Add a cookie consent banner gating analytics — not required today with zero tracking, but becomes required the moment GA4 lands Analytics
all 8 pages Change the footer column headings (SERVICE, COMPANY, TURNAROUND TIERS) from H4 to H3, or make them non-heading elements, so no page skips a level Headings
/contact Add H2 section headings to the contact page — it currently goes H1 straight to H4 with nothing between Headings
4 pages Work a primary keyword into the H1 on /services, /how-we-work, /pricing and /contact without losing the current voice Headings
all 8 pages Raise nav, footer and table CTA hit areas to a 48x48 minimum with 8px spacing (20-32 elements per page currently fall short) Mobile
Medium 15 items
llms.txt Add /llms.txt with company name, one-paragraph description, the service lines, the panel and volume proof points, and links to all 8 pages LLM Readability
/ Add explicit width and height to both client logo images (imbro.png, nyimaging.png) Image SEO
/how-we-work Add explicit width and height to ct-slice-2.png Image SEO
/our-radiologists Add explicit width and height to cxr.png, mri.jpg and petct.jpg Image SEO
public/assets Convert all 6 images to WebP with fallbacks and re-compress cxr.png (currently 484KB, the single largest asset on the site) Image SEO
all 8 pages Add LinkedIn, X and Facebook profile links to the footer and mirror them in the Organization schema sameAs array Social
all 8 pages Add a visually-hidden skip-to-content link as the first focusable element, targeting the main landmark Accessibility
/contact Expand from 195 to 400+ words — what happens after you submit, onboarding timeline, what to have ready, and coverage and response expectations Content Quality
/pricing Work a primary keyword into the first 100 words of body copy (currently none appear) Content Quality
all 8 pages Raise "teleradiology" from 0.09-0.26% toward the 1-3% band, naturally, across body copy and headings — it is the primary commercial term and is currently near-absent Content Analysis
all 8 pages Raise text-to-code ratio above 25% (currently 4.1-17.1%) — largely resolves itself once prerendering ships real copy alongside the markup Content Analysis
all 8 pages Publish a phone number in the footer and on /contact — healthcare buyers expect a callable line, and it is a prerequisite for contactPoint schema Local Business
/about, /contact Publish a business address (or an explicit statement that operations are distributed) to support the Organization schema Local Business
all 8 pages Move inline styles into the stylesheet — 26 styled elements on the home page, 3 on each other page Email & Security
site-wide Re-run PageSpeed Insights for a field INP reading once the daily quota resets (returned HTTP 429 during this audit) Performance

Your Next Steps

These items need an account or credential and cannot be completed without you.

Google Search Console — already done

No action needed. The DNS TXT record google-site-verification=6XJWVOqHL_73fhFX… is live on astrarad.com, so the property is already verified. Once sitemap.xml ships, submit it under Indexing > Sitemaps and the pipeline is complete.

Google Analytics 4 — measurement ID needed

Nothing is tracking today. Without this there is no way to prove any of the work below moved traffic.

  1. Go to analytics.google.com and sign in
  2. Click Admin (gear icon, bottom-left)
  3. Click Create > Property
  4. Enter "AstraRad" and https://astrarad.com
  5. Set time zone and currency, click Next, pick a business category, click Create
  6. Under Data collection click Web, enter the URL and stream name, click Create stream
  7. Copy the Measurement ID (starts with G-)
Paste back: the G- ID, and it gets wired into all 8 routes with SPA route-change pageview tracking.

Google Tag Manager — optional container ID

Only worth it if you want to add or remove tracking tags without a redeploy. GA4 alone is enough to close the analytics gap.

  1. Go to tagmanager.google.com
  2. Click Create Account, enter account and container names
  3. Choose Web as the target platform, accept the terms
  4. Copy the Container ID (starts with GTM-)
Paste back: the GTM- ID. Skip this entirely if GA4 covers your needs.

Social profiles — URLs needed

Three social checks fail and the Organization schema sameAs array cannot be populated without real profile URLs. A LinkedIn company page matters most for a clinical B2B buyer.

Paste back: the LinkedIn company page, and the X and Facebook URLs if they exist. If a profile does not exist yet, say so and it will be marked not applicable rather than failing.

Phone number and address

Neither appears anywhere on the site. Both Local Business checks fail, and the Organization schema cannot carry a contactPoint without a number.

Paste back: the sales phone number to publish, and a business address — or confirm that operations are fully distributed and no address should be shown.