Local SEO. Contractors
Contractor Project Pages: Turning Job Photos Into Local SEO
Written by Ram Kr Shukla, SEO and growth consultant
Quick answer
A contractor project page is one page per finished job: the problem, the work, the materials, before and after photos, and the town it was in. Build a projects hub that links to every job, link each job to its service page and its location page, and strip GPS from every photo before you upload it. Use plain image tags with descriptive file names, one sentence of alt text and compressed files. For schema, use what Google supports: Article and BreadcrumbList, plus VideoObject for walk-through video. There’s no special rich result for projects, and the customer’s street address never goes on the page.
Most contractor websites have one of two things where the work should be. An empty gallery. Or a slider of stock roofs that could belong to any company in the country. Neither ranks, and neither convinces a homeowner who’s about to let a stranger onto their roof.
One contractor I work with, a US roofing and exteriors contractor, had a gallery page titled Album with no job photos on it at all. Its shared drive, meanwhile, held 73 before and after shots from five recent jobs, plus drone video and an older archive of repair photos. The proof existed. It just never left the drive.
This guide is how I turned that folder into a projects hub and a set of project pages, and what I’d tell any roofer, remodeler or exterior contractor doing the same. You’ll get the page structure, how to read a job’s location from photo EXIF data without exposing a customer, image SEO, the structured data Google actually supports for this content, and how project pages should link to your service and location pages. The wider strategy sits in my roofing SEO and construction SEO work. This post stays on the project pages themselves.
Why a Gallery Doesn’t Rank and a Project Page Can
Google reads images through the text around them. Its image SEO best practices say it pulls information about an image from the page content, captions included, and ask you to place images near relevant text. A gallery is the opposite: forty thumbnails and a heading. There’s nothing for a search like “low slope roof replacement” plus a town name to match.
Sliders are worse. Many theme sliders and before and after widgets load photos as CSS background images, and Google’s own guide is blunt about those: Google doesn’t index CSS images. So the one place a contractor put real photos can be invisible to Google Images entirely. Check yours. Right-click a slide, inspect it, and look for an img tag with a src attribute.
A project page fixes both problems. It’s one job, told in text, with the photos sitting next to the words that explain them. It matches long-tail local searches because it names the job type and the town. And it’s first-hand experience on the page, which Google’s helpful content guidance asks about directly when it talks about first-hand expertise.
There’s a sales reason too. A homeowner comparing three roofers wants to see a roof like theirs, in a town near theirs, done by the people who’ll show up. A stock photo can’t do any of that.
What Two Contractors’ Photo Libraries Actually Held
Before you plan pages, find out what you’ve got. Here’s what came out of two very different companies.
The roofing and exteriors contractor
Five recent jobs had usable sets. The photo counts per job looked like this:
16 before shots and 9 after, shot by drone. The strongest set, and it had never been used anywhere.
6 before and 11 after, plus a drone video file.
5 before and 12 after. The most after shots of any job in the set.
7 before and 4 after, all HEIC files, with no GPS in them.
2 before and 1 after. Not even clear what the job was from the photos. Parked.
Repair close-ups, flashing, ridge vents and siding shots, with single siding photos at 30 to 40 MB each. Plus a few personal photos that had no business on a website.
That inventory decided the build. Four jobs became project pages. One set of dormer repair photos became evidence on a new repair service page instead, filed under its parent service. And four more planned pages are still waiting on the owner to confirm what the jobs actually were, because I won’t write a page about work I can only guess at from three photos.
The lawn care and mosquito control company
This one had a professional shoot: 201 frames, all taken on one day in late spring. About 51 unique frames ever made it into the website’s media library. So roughly three quarters of the company’s best photography had never been published.
The full-resolution files ran 6 to 16 MB each. A separate web-sized set ran 400 KB to 1.5 MB, but it was missing frames, so the real library and the convenient folder weren’t the same thing.
Two lessons came out of it that apply to any contractor. First, one shoot means one season. There wasn’t a single fall or winter frame, and a winterizing article ended up with a lush midsummer lawn as its hero image. The alt text described the lawn accurately and still gave no hint of the season, so it sailed through review. So look at the picture itself before you sign off on it.
Second, provenance. One image in the media library didn’t come from the shoot at all. It had a hand-written SEO file name and nobody could say where it came from. Until someone confirms that, the company can’t honestly claim every photo on its site is its own. If your project pages promise real job photos, every photo on them has to be one. A single stock roof slipped in among them undoes the claim for the rest.
Pick the Jobs That Deserve a Page
Not every job deserves a page. Thin project pages with three photos and eighty words do more harm than good, because they dilute the hub and give Google nothing to rank. Use these filters.
Every roofing set that became a page had at least 4 after shots and enough befores to show the problem. The job with 2 befores and 1 after waited.
If you’d rather sell replacements than patch jobs, publish replacements first. Project pages pull in more of whatever they show.
A job 90 minutes outside your normal radius tells Google and buyers the wrong story about where you work.
What was wrong, what the crew found when they opened it up, what you did about it. If nobody remembers, don’t invent it.
Ask when the contract’s signed. Most homeowners say yes to photos of the roof. Fewer want their name or face on it.
Ten identical shingle replacements read like a template. Mix job types, roof types and problems.
One more filter that’s easy to miss on commercial work: the customer’s own brand. On the commercial flat roof job, the building belonged to a business with a recognisable name. I kept that name out of the URL slug and the copy entirely. The page describes the building type and the problem. Your customer didn’t sign up to be your case study’s headline.
Build the Projects Hub First
The hub is the parent page every project hangs from. Think of a URL like https://www.example.com/projects/ with each job sitting beneath it as a child page. That hierarchy does three jobs at once: it gives you a clean breadcrumb, it keeps every project two clicks from the homepage, and it gives Google one obvious page that lists all your work.
What the hub needs:
- An opening that says how you document jobs. Who takes the photos, when, and what you publish (town, never the street). That’s trust copy, and it’s unique to you.
- Jobs grouped by service. Roof replacement, low-slope and flat roofs, commercial, repairs. Each group gets a short intro and its project cards.
- Cards that are real links. Each card: one after photo, the job type, the town, one line on the problem, and a plain anchor link to the project page.
- Filters as links, if you have any. A filter built from JavaScript buttons gives Google nothing to follow. I’ve written up why links Google cannot follow quietly block whole sections.
- Links from pages people already visit. On the roofing site, the about page and the reviews page both link to the hub. Put it in your main navigation too.
Depth matters more than people expect. The first draft of the roofing hub was 873 words. The best competing hub I benchmarked ran to about 2,800 words, and one outlier hit 8,500. I expanded the hub to 2,934 words: section intros, how each job type differs, what homeowners should ask before hiring. I didn’t chase the 8,500. That page was an outlier, and matching it would have meant padding.
Keep the hub from fighting your service pages. Title it as a projects page (“Roofing Projects and Recent Jobs”) and let the service page own the head term. If you’ve already got overlapping pages, my guide on keyword cannibalisation shows how to spot it in Search Console.
What Every Project Page Needs
Here’s the structure I used on each roofing project page. Treat it as the minimum, then add whatever makes that job different.
“Low-Slope Roof Replacement in” plus the town. It’s the query you want, and it’s honest.
Leaks in the back bedroom after storms. Granules in the gutters. Start where the customer started.
Rotted decking around the chimney, failed flashing, a second layer of shingles nobody knew about. This is where your expertise shows.
Tear-off, deck repair, underlayment, flashing, ventilation, shingles, clean-up. Short paragraphs, one per stage.
Shingle or membrane type, brand and colour if you know them. If you don’t, leave it out.
Matched angles where you can, each pair with a caption saying what changed.
Days on the roof and who led the job. Both need the owner’s input.
Town and county. Nothing narrower.
One or two sentences in their words, first name and town at most.
Up to the hub, across to the service page and the location page, and a clear call to book an inspection.
Some of those fields need the business owner. On the roofing pages, four things are still open: the named crew lead, a customer quote, days on site and the shingle brand and colour per job. I left them blank. In their place, each page carries a short company facts block (years in business, manufacturer certification, Better Business Bureau listing, workmanship warranty), which is true, checkable and doesn’t need anyone’s memory.
On length, the finished roofing project pages run from about 1,350 to 3,340 words. The competing project pages I benchmarked ran from about 850 to 3,200 words, and each of mine was pushed past the main competitor for its job type. The longest is the commercial job, because commercial buyers want the detail: parapet walls, coping and exactly what failed.
Don’t reuse paragraphs between projects. If two jobs share a section word for word, rewrite it from the job notes or cut it. Templated project pages fall into the same trap as templated location pages.
Reading Job Locations From EXIF Without Exposing the Customer
Phones and drones write GPS coordinates into every photo by default. That’s useful to you, because it tells you exactly where each job was, even when nobody labelled the folder. It’s also a liability, because the same coordinates point at a customer’s front door.
On the roofing jobs, the drone shots carried GPS, so I could read the town for each job straight from the files and confirm it with the client where it mattered. The commercial job’s HEIC files had none. For that one, the town is still a question for the crew, and I didn’t guess it from the scenery.
How to read it
On a Mac, Spotlight already indexes photo metadata, so mdls reads it without installing anything. Anywhere else, use ExifTool, the standard free metadata tool:
# Mac, built in
mdls -name kMDItemLatitude -name kMDItemLongitude job-photo.jpg
# Any platform, ExifTool
exiftool -n -gpslatitude -gpslongitude job-photo.jpg
Look the coordinates up on a map, write down the town and county in your job sheet, and stop there. The coordinates themselves never go into a page, a caption, a file name, a spreadsheet you share or a schema field.
What never gets published
- The street name, house number or coordinates, anywhere on the page or in the markup.
- A map pin or embedded map of the job.
- Frames where a house number, street sign, mailbox, licence plate or the neighbour’s house reads clearly. Crop or drop them.
- Faces, unless that person has said yes in writing.
- The customer’s full name. First name and town, with permission, is plenty.
Strip GPS before you upload
It isn’t enough to keep the address out of the copy if the image file still carries it. WordPress keeps the original file you upload: since version 5.3 it creates a scaled copy of big images but stores the original in the uploads folder. Anyone who opens that file can read its metadata. Apple’s own personal safety guide warns that people you share photos with may be able to read the location and learn where the photo was taken.
So keep two copies. The field copy, with GPS, stays private in your job folder so you can always look up where a job was. The web copy gets stripped before it goes anywhere near your website or your Google Business Profile.
# Remove only the GPS block from the web copies
exiftool -gps:all= -xmp-exif:all= -overwrite_original web/*.jpg
# Check: this should print nothing
exiftool -a -G1 "-gps*" web/*.jpg
Those two groups hold the location data. Credit and copyright live in other fields (IPTC and other XMP groups), so they survive, which matters later. Check drone video files the same way. Many export tools drop all metadata anyway, but don’t assume it. Run the check. On an iPhone, you can also switch off location in the share sheet’s options before sending photos from the field, though for your own records you’ll usually want the GPS kept on the original.
Does geotagging help rankings?
You’ll see advice to geotag photos for local SEO. Google’s image guide talks about file names, alt text, captions, page context and image sitemaps, and says nothing about GPS coordinates in files. The location signal you control belongs in visible text: the headline, the body, the caption. Publishing coordinates trades a customer’s privacy for a ranking boost there’s no documented reason to expect.
Image SEO for Job Photos
Once the photos are clean, make them easy for Google to understand and quick for a phone to load. Most of this comes straight from Google’s image guide.
File names
Google says file names give it very light clues, and gives the example of my-new-black-kitten.jpg beating IMG00023.JPG. Light clues are still clues, and it costs nothing. Rename before upload: low-slope-roof-before-tear-off-01.jpg, low-slope-roof-after-edge-detail-02.jpg. Short, descriptive, lowercase, hyphens between words.
Alt text and captions
Write alt text for someone who can’t see the photo. One sentence, describing what’s actually in the frame: “Rotted roof decking exposed around a brick chimney after tear-off.” Don’t paste the town and the service name into every alt attribute. That’s keyword stuffing, and Google’s guide warns against it.
Captions do the contextual work. That’s where the town, the job and what changed go, and Google says it reads captions as part of the page content.
Size, format and loading
- Resize. A 30 to 40 MB siding photo or a 6 to 16 MB shoot frame has no place on a web page. Export to about 2,000 pixels on the long edge, which usually lands at a few hundred KB.
- Use a format Google supports. Its list is BMP, GIF, JPEG, PNG, WebP, SVG and AVIF. HEIC isn’t on it, so convert phone HEIC files before upload.
- Serve sizes with srcset. WordPress builds responsive sizes for you. Custom sliders often don’t.
- Set width and height. It stops layout shift as photos load. I cover this in fixing Core Web Vitals at the template level.
- Lazy-load below the fold only. The first after photo is usually your largest element on screen, so let it load straight away.
Before and after layout
Use two plain img elements side by side, or one above the other on phones, with a caption under the pair. A drag slider looks clever, but if it renders the photos as CSS backgrounds you’ve hidden your best evidence from Google Images. If you must use a slider, check that both photos exist as img tags in the page source.
If your project gallery loads through JavaScript, an image sitemap helps Google find the files. For plain HTML pages it’s optional. And since project photos often double as Google Business Profile photos, the same clean web copies are what you should be posting there. My sibling post on Google Business Profile posts covers that side.
Structured Data: What Google Actually Supports Here
Let’s clear this up first. There’s no Google rich result for a project, a portfolio or a case study. Plugins will happily output markup with those words in it, and none of it earns anything in Google Search. Stick to types Google documents, and follow its rule from the structured data guidelines: don’t mark up content that isn’t visible on the page.
Google’s Article markup has no required properties. Add headline, author and image. Use your best after photo: at least 50,000 pixels (width times height), ideally in 16×9, 4×3 and 1×1 crops.
Home, Projects, the job. It mirrors the hub hierarchy and tells Google where the page sits in your site. See Google’s breadcrumb docs.
A drone walk-through embedded on the page can be marked up with video structured data, with a name, a thumbnail and an upload date.
Google’s image metadata feature reads creator, credit and copyright, either as markup or as IPTC fields embedded in the file. Stripping only GPS keeps those fields intact.
LocalBusiness requires a name and an address, and that address is yours. It usually belongs on your homepage or contact page. Never put a customer’s address into any schema address field.
Google’s review snippet rules make self-serving reviews on LocalBusiness and Organization pages ineligible for stars. Keep the customer quote as visible text and skip rating markup.
What about HowTo and FAQ markup? Google stopped showing How-to rich results in 2023, and FAQ rich results now show only for well-known, authoritative government and health sites. Google says unused markup causes no problems, but it won’t show anything for a contractor either. If a project page has a genuine FAQ, keep it for readers and don’t expect a visual feature from it.
A minimal project page block looks like this (swap in your own URLs and photo):
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Low-Slope Roof Replacement in [Town]",
"image": ["https://www.example.com/wp-content/uploads/low-slope-roof-after-01.jpg"],
"author": {"@type": "Organization", "name": "[Your Company]"},
"about": "Low-slope roof replacement"
}
Add a BreadcrumbList block beside it. For the broader picture of which types most businesses need, see the six schema types most businesses actually need.
Linking Project Pages to Service and Location Pages
A project page that nothing links to is an orphan, and orphans rarely get crawled often or ranked well. My sibling post on location pages that never get indexed goes deep on that failure. Project pages need links in four directions.
Every project links to the projects hub, and the breadcrumb does it again.
The low-slope job links to the low-slope or flat roof service page, with anchor text naming the service. The service page shows 2 or 3 recent jobs back.
The job links to the page for its town, if you have one. That location page lists the jobs done there.
On the roofing site, each project page got 3 or 4 contextual links from existing pages on related topics: one sentence each, placed where it reads naturally.
The location page link matters more than it looks. The usual problem with location pages is that they’re thin city-name swaps, and Google filters them. A list of real jobs done in that town, with photos and a line on each, is the most specific local content you can put on one. I’ve covered the rest of that in how to make location pages unique.
Why push internal links this hard? On the roofing site’s last full crawl, only 11 of 335 URLs had any referring domains at all. External authority was close to zero, so internal links were the one lever fully under our control. If that’s your situation too, this internal linking system is how I route what authority there is.
Two cautions. First, use descriptive anchors (“the low-slope roof replacement we finished last month” style) and skip “click here” or the bare town name. Second, keep the head term on the service page. A project page should never be titled “Roof Replacement” plus the town if your service page already targets exactly that.
How to Turn a Job Folder Into a Project Page, Step by Step
This is the process I follow for each job, in order. Most of the time per page goes into the writing.
How to Tell Whether Your Project Pages Are Working
Give new project pages a couple of months before judging them, then look in three places.
- Search Console, Pages report. Filter pages containing your projects path and watch impressions and the queries behind them. You want job-type and town combinations showing up.
- Search Console, Image search type. Switch the search type to Image and see whether your job photos appear at all. If they never do, check for CSS backgrounds or blocked image folders.
- Leads that mention a job. Ask new customers which page they saw. “The roof on the street behind mine” is the best project page review you’ll get.
On the roofing site, the honest status is that the pages are too new for ranking data. What I can tell you is the baseline they’ll be judged against: the last full crawl export showed 73,010 impressions and 211 clicks across the site, a 0.29% click-through rate. So the site was being seen and mostly skipped. Specific, photo-led pages are part of the answer to that, alongside titles, reviews and the profile work in my local SEO services. For finding which service pages are missing in the first place, see the sibling post on gap analysis in Search Console.
Contractor Project Pages FAQ
What is a contractor project page?
It’s one page about one finished job: what was wrong, what your crew found, the work and materials, before and after photos, and the town where it happened. It sits under a projects hub and links to the matching service page and location page.
Is a project gallery good for SEO?
Not on its own. A gallery is mostly images with very little text, so there’s nothing for a local search to match, and sliders built on CSS background images don’t get indexed by Google at all. Keep the gallery if you like it, but give each good job its own page.
How many photos should a project page have?
Enough to show the before, the work and the after, which usually means 6 to 12. In the roofing jobs I worked from, every set that became a page had at least 4 after shots. A job with 2 befores and 1 after waited.
Should I put the customer’s address on a project page?
No. Publish the town or county and nothing narrower: no street, no house number, no map pin, no GPS coordinates. Crop house numbers and plates out of the frames, and use a customer’s name or quote only with written permission.
Does geotagging photos help local SEO?
Google’s image SEO guide doesn’t list photo GPS data as something it uses, and it does give clear guidance on file names, alt text and captions. Put the town in visible text and strip GPS from the files, because published coordinates can point straight at a customer’s home.
What schema should a contractor project page use?
Article or BlogPosting with the best after photo as the image, plus BreadcrumbList for the hub path, and VideoObject if a walk-through video is on the page. There’s no project or case study rich result. Keep LocalBusiness for your own business details, never the customer’s.
How should project pages link to service and location pages?
Each project page links up to the hub and across to its service page and the location page for its town. Those pages link back: the service page shows 2 or 3 recent jobs, and the location page lists the jobs done in that town.
How long should a project page be?
Long enough to tell the job properly, and longer than the pages you’re competing with. The roofing project pages I built run from about 1,350 to 3,340 words, against competing project pages that ran from about 850 to 3,200.
Related reading
For the service side, see my roofing SEO and construction company SEO pages. On the local foundations: local and Google Business Profile SEO for service businesses, multi-location pages without duplicate content and why location pages never get indexed. On the technical side: winning image packs and other SERP features and flattening crawl depth.
Tags: Local SEOContractor SEOImage SEORoofing SEO
Written by Ram Kr Shukla
SEO and growth consultant with 20+ years in SEO and growth and 50+ brands behind him. Google Analytics and Google Ads certified. Works as an SEO consultant or Fractional Chief Digital Officer, and runs local SEO for US contractors and home service companies, from roofing and exteriors to lawn care.




