How to Read Your Server Access Logs After a Hack

Invasion Investigation · Part 3

The last step outside

Logs feel like they belong to the server, so this feels like going inside. It isn’t.

An access log is a record of visits, not the scene itself. It’s the camera footage, not the room. Reading it changes nothing, disturbs nothing and tells nobody you’re looking. You’re still on the perimeter — just at the last position before the tape.

But there’s one urgent difference from Parts 1 and 2, and it’s the reason this post exists now rather than later.

Download them today. Read them next week.

Everything else in this series can wait. This can’t.

Most hosts keep between seven and thirty days of access logs and then delete them permanently. Some keep the current file and one archive. No warning, no recovery, no appeal.

Acquiring is not analyzing. Get the files off the server today, put them somewhere that isn’t the server, and read them when you have time. Rushing the analysis is how you reach wrong conclusions; rushing the download is how you have anything to be wrong about.

From the case file. The compromise in this series began 12 September 2024. The earliest surviving access log covers 10 February 2026 — seventeen months later. Everything covering the actual break-in had already been rotated away before anyone knew to look.

The consequence is permanent. The entry point in this case can be narrowed, argued and supported — but it can never be proven, because the only evidence that would have settled it no longer exists. Not through any failure of the investigation. It simply aged out.

Where to get them: in cPanel, Metrics → Raw Access. Download everything offered, including the archived files, and do it for every domain in the account — not just the one that raised the alarm. With SSH, copy the whole log directory.

And one thing a non-technical owner can do right now that genuinely matters: open a support ticket asking your host to preserve existing logs and extend retention. It costs a sentence. In this case it would have been worth more than every other action combined.

The rest is background scanning

After the crawlers, most of what’s left is the internet knocking on every door on the planet, continuously, forever. Every public web server gets this. Yours is not special and it does not mean you’re under attack.

Vulnerability scanners walk a list of known paths — /wp-login.php, /xmlrpc.php, /phpmyadmin, plugin readme files.

Secret hunters ask for things that should never be public: .env, .git/config, pass.txt, config.json, stray backup files.

From the case file. In this site’s logs, a single address using a command-line tool requested /cgi-bin/.env and /cgi-bin/pass.txt seconds apart, got errors for both, and moved on. Pure background radiation, and it had nothing to do with the compromise.

Proxy scanners are testing whether your server will relay traffic for them. They’re easy to spot once you know the shape: a CONNECT request, or a GET where the request line contains a full URL to somebody else’s website rather than a path on yours. That’s someone checking whether they can launder their traffic through you.

The principle that sorts all of it

Here’s the thing that turns an overwhelming file into a readable one:

Filter by outcome, not by intent.

Thousands of hostile-looking requests that all returned 404, 403 or 500 are weather. They tried, your server said no, nothing happened. You can set the entire category aside without reading a line of it.

What matters is anything that succeeded. One 200 in that pile is worth more than the ten thousand failures around it — because it means something answered.

That applies straight across. A proxy test that got refused is noise; a proxy test that got a 200 means your server may be relaying somebody else’s traffic, which is its own serious finding. A scanner asking for .env and getting a 404 is nothing; the same request returning content is the whole case.

Sort by status code first. The failures are the crowd. The successes are the story.

Before anything else: know what normal looks like

A number in a log means nothing without a baseline. This is where most people go wrong on the first page.

Pick a quiet week from before the compromise and note your ordinary volume — requests per day, redirects per day, and the handful of addresses that appear constantly. That’s your reference.

From the case file. This site normally served between 300 and 800 redirects a day. On 30 March 2026 it served 10,294. That number is only meaningful because the ordinary number was known — and it turned out to be a misconfiguration by the hosting company, not the intruder.

It cuts the other way too. Another day showed 172 requests to a single file, which looked like meaningful crawler activity until the baseline showed real crawler traffic ran at about 23 a day — and all 172 were the site’s own scheduled-task file being rejected by a countermeasure the owner had installed himself.

Then subtract yourself

Before you look for an intruder, remove everyone who belongs there. This step saves more time than any other, and skipping it is how people spend a week chasing their own hosting company.

Your own address. Find it, and exclude it. Every dashboard session you’ve had is in that file.

Your host’s infrastructure. Provisioning tools, control-panel single sign-on, backup agents and malware scanners all generate traffic from your host’s own address ranges, hitting paths you’ve never heard of.

Your plugins. Security plugins in particular are, by design, the most attacker-shaped legitimate traffic on your site — they scan, they sync, and they call home from cloud fleets that appear as many different addresses.

Your monitoring. Uptime checks every one to five minutes from rotating addresses. Cache preloaders that request every URL on the site in a burst.

From the case file. Six separate findings in this investigation turned out to be the owner’s own software or his hosting company. The most convincing was 209 POST requests to a single PHP file, from eighteen different IP addresses, each carrying a randomised parameter — a textbook backdoor signature.

It was a security plugin. The proof was in the same log file: the plugin’s own activation request appears forty-one seconds earlier. Nothing in the suspicious lines identified them. The line before them did.

Read around the hit, not just the hit. Searching for a pattern finds the pattern and hides the explanation sitting next to it.

What a log line actually says

Every line is one request. Stripped down, it records who asked, what they asked for, and what your server answered:

203.0.113.44 - - [01/Mar/2026:06:29:47 -0700] "GET /cgi-bin/.env HTTP/2.0" 500 716 "-" "curl/8.7.1"

Reading across: the address that asked, the date and time (with the timezone — note it), the method and path requested, the status code your server returned, the size of the response, the referrer (the page they came from, - if none), and the user agent — what the visitor claims to be.

The status code is the answer, and it’s the field most people skip.

The codes that matter

Code Plain English What it means in an investigation
200 OK — here it is Something answered. The most important code in the file
301 Moved permanently A redirect. One you didn’t create is a hijack
302 Moved temporarily Normal after a successful login
304 Not changed since last time Caching. Noise
403 Refused Your protection working
404 Not found Nothing there. Nothing happened
410 Gone, permanently and deliberately A page that was removed on purpose
500 The server broke Something is failing, and it matters more than it looks
503 Temporarily unavailable Maintenance or overload

200 is the one that counts. Every other code is a version of nothing happened. A 200 means content went out the door.

404 and 410 are not the same, and the difference is money. A 404 says “not here” — Google keeps checking back for months in case you put it back. A 410 says “gone, deliberately, don’t ask again” and clears far faster. When you eventually remove thousands of spam URLs, 410 is the code you want.

301 is the one never to use on spam. A permanent redirect tells search engines to carry the old URL’s signals over to wherever you point it. Redirect spam URLs to your homepage and you’ve asked Google to associate that content with your front page.

And 500 is the quiet disaster. A server error tells Google “something’s broken here, I’ll try again later” — so the URL is retained rather than released. If a cleanup rule fails and starts throwing errors instead of 410s, the removal doesn’t just stall; it reverses.

From the case file. That is exactly what happened here. The rule correctly returning 410 for the spam URLs broke on 1 March 2026 and began returning 500 instead. For as long as it was broken, every request was telling Google to hold on to a URL the site was trying to get rid of. The fault was invisible from the front page and cost weeks of recovery.

A separate misconfiguration produced 10,294 redirects in a single day on 30 March — against a normal rate of a few hundred — and that one turned out to be the hosting company, not the intruder.

Both of those were found by looking at status codes, and neither was visible any other way.

Most of your log is spiders

Open the file and the first impression is chaos. That’s normal, and it’s mostly crawlers.

Googlebot and Bingbot, then the long tail nobody thinks about: Ahrefs, Semrush, Majestic, DotBot, MJ12, Applebot, Yandex, Baidu, PetalBot, and the newer AI crawlers — GPTBot, ClaudeBot, PerplexityBot, CCBot, Amazonbot. On a small business site they routinely outnumber human visitors several times over.

From the case file. A single crawler — Meta’s — accounted for 4,486 requests in one month on one domain in this case. That’s one bot, on one site, in one log.

So bucket by user agent before you do anything else. Count requests grouped by user agent string and you’ll usually eliminate the large majority of the file in a single pass. What’s left — real visitors and anything automated pretending not to be — is a far smaller set worth reading.

But don’t just delete everything that says “Googlebot”

Here’s where noise removal turns into detection, and it’s the best trick in this post.

A user agent is a claim, not an identity. Anyone can set that header to anything. Real Googlebot passes a reverse DNS lookup that resolves to googlebot.com and then forward-confirms back to the same address. Anything calling itself Googlebot that fails that test is something else wearing the name.

And there’s a specific reason an intruder would wear it. Cloaking works by checking the user agent — serve spam to search engines, serve the real site to everyone else. Which means the operator has to impersonate a crawler to test their own work. They can’t verify the spam is being served without asking for it the way Google would.

From the case file. The rewrite rule on this site keyed on a short list of strings in the user agent — googlebot, google, yahoo, aol. Any request carrying one of those words was served the spam. Anyone at all could trigger it, including the people who built it.

So the thing to look for is precise and rare: a request claiming to be a crawler, failing verification, asking for one of the spam URLs you identified in Part 1. That’s not a search engine. That’s somebody checking their deployment — and it will be coming from an address worth writing down.

Finding the first thread

You need one solid starting point. There are two ways in, and which you use depends on what you already have.

If you have a filename, start there

The fastest opening move in this whole process: search the log for a suspicious filename, and keep only the lines that returned 200.

You may already have a name — from a scanner, from something you spotted on the server, or from the list of shapes further down this article. Search for it, filter to successes, and you have proof the file existed and was used, plus the exact times it was used.

Then read the address in those lines. That’s your thread.

This is where most real investigations actually begin, and it needs no prior knowledge of anything else in this post.

If you don’t have a filename, start by subtraction

No candidate name? Then work the other direction — remove everyone who belongs, and look at what’s left.

Look for addresses that don’t belong to your host, your vendors, a verified crawler or you — hitting paths that have no business being hit. Cloud hosting ranges are worth particular attention: a request from a rented server, to an administrative endpoint on a small business website, has no innocent explanation.

Either way, pull the whole session

Both routes converge on the same next step, and it’s the one that turns fragments into a picture.

Once you have an address, filter the entire log set to that address alone and read it in order.

Every request they made, in sequence, across every domain in your account. Scattered oddities become a session with a shape — arrival, reconnaissance, deployment, return visits. And the timestamps in that session become the frame you check everything else against.

And the address you find is probably real. Not real as in someone’s home — real as in the machine that actually did the work. Anonymising every request through a proxy chain costs time, and automated tooling running a sequence at machine speed generally doesn’t bother, because it doesn’t need to: the box is already rented, disposable and not in the operator’s name. The rental is the anonymity.

That still makes it worth writing down. A rented server has an owner, and that owner has an abuse contact who can take it offline — which helps the next victim more than it helps you, but it is the one lever that exists. Look it up: who owns the block, what network it sits in, whether it appears in public abuse databases. Ten minutes, no server access, and it’s the closest thing to a name you’re going to get.

If you host several domains, do it across all of them at once. The same address appearing on unrelated sites in your account is lateral movement, and it’s visible only when you hold both logs together — neither file shows it alone.

Look for spurts

Here’s the signature worth learning, and it’s simple enough to spot by eye.

Four or five requests from one address, seconds apart, to different endpoints.

That’s not a person. A human browsing produces a page request, then a pause, then a link from that page. A tool produces a rapid sequence of unrelated requests with no pause and no path between them.

The distinction in one line: humans follow links. Tools follow lists.

Which gives you two checks beyond the timing. The referrer field — a real visitor’s second request usually names the page they came from; a script’s requests usually carry nothing. And reachability — ask whether each request was even possible from the one before it. If nothing on the previous page linked to it, the visitor didn’t find it by looking.

From the case file. On 10 February 2026 the operation redeployed itself. Read purely as write times, in order:

06:46:44 – 06:46:58 — WordPress core rewritten. 1,148 files in fourteen seconds. 06:54:34 — a robots.txt declaring twenty sitemaps. 07:06:56 – 07:07:00 — two file-manager plugins. 1,414 files in four seconds. 07:07:14 — a new administrator account.

Twenty and a half minutes end to end — but the work inside it adds up to under twenty seconds. Everything else is gaps.

That shape is the finding, before anyone examined a single file. Not “fast” — bursts of thousands of files sharing one timestamp, separated by idle stretches. A person doesn’t write 1,148 files in fourteen seconds, and a person isn’t idle for eight minutes in the middle of their own break-in.

And this is the thing to hold on to: almost none of it was done by hand.

The tempo is not unusual for this kind of compromise — it’s the norm, because the whole operation is tooling. In this case the payloads were machine-packed with a different number of layers per file. The administrator accounts were created by a bulk tool that takes a list. The rewrite rules were deployed by another bulk tool. A third tool existed purely to replicate a payload into many directories at once. The spam pages weren’t written; they were generated on request. Even the site itself was a pre-built image, stamped out across other victims with the same default title still on it.

That matters for two reasons. Practically, speed and sequence are your most reliable tells — software has a cadence that people don’t. And personally: a reader looking at this in their own logs is looking at a machine working through a list, not a person who chose them. You weren’t selected. You were reachable, and the tool was running.

Bursts have innocent causes too — a page load pulling twenty assets, a dashboard session hammering its own AJAX endpoint, a crawler working through a sitemap. The discriminator is whether the burst hits unrelated things. Twenty images from one page is a page. Four unrelated administrative endpoints in six seconds is a script.

Look for a login that worked

If they hold valid credentials, they don’t need an exploit. They log in the way you do — and their traffic looks exactly like yours, because it is the same traffic. The only things that separate you are the address it came from and the hour it happened.

Everyone’s /wp-login.php is hammered continuously by brute-force bots. That’s weather. The signal is a login that succeeded, and WordPress makes it readable if you know where to look:

  • A failed login re-displays the form. POST /wp-login.php200
  • A successful login redirects to the dashboard. POST /wp-login.php302

That’s the default behaviour, though security plugins and custom login pages can change it — so don’t hang everything on the code alone.

The reliable proof is what comes next. After a successful login, the same address starts pulling /wp-admin/ pages and those come back 200. A failed login never produces that; it can’t, because there’s no session. So the pattern to look for is the pair:

a POST to wp-login.php, immediately followed by /wp-admin/ requests returning 200, from the same address.

That holds regardless of how the login page itself is configured, and it’s unambiguous. Somebody got in.

On a one-person site, every address doing that should be yours. Then follow each session forward — the pages requested tell you what they came for. users.php and plugins.php mean someone is taking inventory. theme-editor.php or plugin-editor.php mean someone is writing code into your site through the browser, which is the single most alarming line you can find in a log.

The absence that tells you the most

Here’s the detail worth carrying away.

A successful login with no failed attempts in front of it means the password was known, not guessed.

Brute force is loud — hundreds or thousands of failures before anything works, and it’s unmistakable in a log. A single POST that succeeds on the first try, from an address you don’t recognise, is a different event entirely. Nobody guessed anything. They already had it, which points at a credential taken from somewhere else rather than a door forced here.

That distinction matters more than almost anything else you’ll find, because it changes what you’re looking for and what you have to fix. Part 6 is where it gets followed properly.

From the case file. The operators in this compromise never needed to brute force anything. They created their own administrator accounts — five over five months, with names escalating from obviously foreign to indistinguishable from a default install — and from then on they simply logged in. Every dashboard action they took was a legitimate authenticated session belonging to a legitimate account, and no security plugin had any reason to object.

Learn their hours

Once you have a candidate address, plot when it shows up. Count its requests by hour of the day across every day it appears.

Automated background traffic — beacons, monitors, crawlers — spreads evenly around the clock. Hands on keyboards don’t. An operator working a list keeps working hours, and those hours belong to wherever they are, not wherever you are. The result is a consistent daily window with silence on either side of it.

Two things fall out of that.

It’s a filter. Once you know the window, you can slice 570,000 lines down to the hours that matter and read what’s left.

And it’s a fingerprint. A window that sits at a strange time in your timezone usually sits at an ordinary time in someone else’s. That won’t identify a person, but it’s a corroborating detail — and when it lines up with other evidence, like the language of comments left in the code, two independent signals agreeing is worth more than either alone.

From the case file. The activity in this compromise arrived at roughly 4am, night after night, consistently enough that the owner started setting an alarm for it.

4am in Georgia is late afternoon in Southeast Asia — an entirely ordinary working hour for somebody else. That’s the fingerprint: a window that looks bizarre in your timezone usually looks like a normal shift in theirs. And it lined up with a second, independent signal — the comments left inside their own code were written in Indonesian.

Two unrelated pieces of evidence agreeing is worth far more than either alone, which is the standard Part 6 is built on.

One caution before you lean on it: check your clock. Server logs, your control panel and your own computer may all be reporting different timezones, and an analysis built on three unlabelled clocks produces correlations that are simply wrong. Note which zone the log is in before you plot anything.

Read the response, not the request

If you take one technique from this article, take this one.

Most people read log files left to right and stop at the URL. The status code sits further along, and it’s where the answers are.

A 200 for a file that isn’t on your disk means something served content for a path that doesn’t exist. That’s a rewrite rule you didn’t write, and it’s the fastest way to find a cloak, a hidden handler or an injected route.

You can’t fully complete this one from the perimeter — confirming a file’s absence means looking at the filesystem, which is the next post. But you can build the list now: every path that returned 200 and that you don’t recognise. That list is the first thing you’ll check when you go inside.

Watch the codes generally. A run of 500 errors marks something breaking. A 301 where you expect a 200 means a redirect that shouldn’t be there.

When the status code is lying

A code tells you what your server said. It doesn’t always tell you what was true. Three patterns are worth knowing, and all three are things you can find by eye.

A 404 followed by a 200 on the same path

This is the sharpest anomaly in a log file, and it has only one explanation.

The first request asked for a file and the server said not here. A short time later the same path was requested again and the server served it. Something created that file in between.

You have just timestamped a deployment. Not approximately — to the second, bracketed by two lines that prove absence and then presence. Whatever wrote it did so in that window, and the request immediately before the 200 is very often the thing that put it there.

Look at what else that address did in the same minutes. This is where a dropper hands off to a payload, and it’s the closest thing to watching it happen.

A file that returns 404 to you and 200 to them

The 404 isn’t always the server’s opinion. It can be the file’s.

Payloads are commonly built to stay quiet unless invoked correctly — they check for a specific parameter, a key in the request, or a particular user agent, and if it isn’t there they return nothing and look like an absence. Probe the path in a browser, get “not found,” conclude it isn’t there, and move on. That’s the intended outcome.

From the case file. The tooling recovered here was built on exactly this principle. Each stage of the packed payload checked a value against a stored fingerprint before doing anything at all. If the check failed, it simply stopped and produced no output. A file with real capability inside it, sitting there behaving like an empty page.

So a 404 in your own browser is not evidence of absence. A 200 in your log for a path that 404s for you is evidence of the opposite — somebody knows the way in and you don’t.

A 200 that displays “not found”

The reverse, and the reason Part 2 sent you to Search Console’s Soft 404 report.

The page body says not found. The status code says success. Humans see an error and leave; search engines see a valid page and index it. That contradiction is the whole mechanism of a cloak, and it’s why clicking a spam URL and getting a “not found” page proves nothing at all.

The rule underneath all three: never conclude from a single code. Read the pair, the sequence, and where you can, the body. One line is a claim. Two lines in order are evidence.

Paths worth looking twice at

You’re reading requests, so what you’re hunting is a path somebody asked for that you never installed — and, per the rule above, one that answered.

Here are real filenames recovered from this compromise. They will not be on your site, and that’s the most important sentence in this section — the kit builds a fresh set per victim. Search your logs for ws70.php, find nothing, and conclude you’re clean, and you’ll be wrong. They’re here to teach the shapes.

A naming family. ws70.php, tx70.php, tf70.php, mas70.php, lm70.php, ah70.php — two letters and a number, six siblings. Malicious files travel in families. Find one and go looking for its relatives, because the operator’s own configuration file listed all six by name.

Impersonators. wp-admin.php, robot.php, robots.php, webindex.php, db-fix-sqlite.php, cleaner.php. Every one is designed to survive a glance.

wp-admin.php is the one to internalise, because it’s checkable without knowing anything: WordPress has no such file. wp-admin is a directory. A request for /wp-admin.php at the root of your site is always worth a second look. Same with robots.phprobots.txt is real, robots.php is not — and cleaner.php, which sounds precisely like something a security tool would leave behind.

Machine-generated strings. kqucusrf.php. j260210_13.php. 4ja276jo6g2pujmhvum473yCdefault.php. Real software is named by people, for people. Eight random lowercase letters is an identifier, not a name.

And look closely at that middle one: j260210_13.php is dated. 260210 is 10 February 2026 — the day it was deployed. They stamped the build date into the filename.

Hidden directories. .hcc.thumbs/, and a plugin folder called 1mno1hoitt9278eu3scq. A leading dot hides a file from most file managers by default, which is exactly why it’s used.

The neatest example in the whole case is a file called .ht.sqlite — a database, named to look like an Apache config file. Apache blocks anything starting with .ht from the web by default, so they used your own server’s security convention to stop anyone downloading their data.

And files that lie about their type. One file in this case had a .php extension and contained no PHP at all — it was an ordinary HTML page. Another was named as an image and was a database. A third set were named emoji1.png, metaicons.jpg and wp-themesall.gif, sitting in a hidden folder under cgi-bin, all of them zero bytes. A zero-byte PNG has no image in it. Whatever those were for, being pictures was never part of it.

What to actually do with this

Don’t search for these names. Search for these properties:

  • A requested path you can’t trace to something you installed
  • A .php file in a directory that should only hold images, uploads or assets
  • Several requests to differently-named files that share a naming scheme
  • A filename that looks like core but isn’t — starting with wp-admin.php
  • Any path beginning with a dot
  • And, per the rule above, any of the above that returned 200

That last one narrows it fast. A hostile request that failed is weather. A request for a file you’ve never heard of that returned success means the file was there and it worked.

Notice what never happened

Absence is evidence, and logs are the only place you can see it.

From the case file. Seven hidden directories were created inside a four-minute window in February. In every surviving log, not one of them was ever requested. That silence is how the investigation established the deployment was interrupted rather than completed — a conclusion no file on the server could have supported.

You can’t search for a request that isn’t there. You find it by comparing what exists against what was ever asked for, which is a question you’ll be able to answer properly after the next post.

Reduce before you interpret

From the case file. The log corpus in this investigation runs to roughly 570,750 lines.

Nobody reads that. The skill isn’t reading logs, it’s reducing them — filtering to one address, one path, one day, one status code, until you have a few dozen lines worth actually looking at.

Sort by address and count. Sort by path and count. Filter to the day the index chart in Part 2 said things changed. Work in slices.

What the logs can’t settle yet

You’ll finish this post with a baseline, a timeline of when traffic changed, a list of addresses sorted into yours, theirs and unexplained, and a list of paths that returned success without obviously existing.

What you won’t have is conclusions. Whether a file is genuine or a lookalike is a question about size and contents. Whether a 200 was real or a rewrite is a question about the disk. Both are inside.

The perimeter produces a prioritised list of questions, and that’s the whole point — because going in with four specific questions about a dozen specific paths disturbs far less than wandering around hoping something looks wrong.

What you carry inside

The log doesn’t just give you filenames. It gives you the full path.

"GET /wp-content/uploads/2024/11/ws70.php HTTP/1.1" 200

That’s not a clue, it’s an address. You now know the exact directory to open, and you can walk straight to it instead of reading 45,000 files hoping something looks wrong.

The location is evidence in its own right. A PHP file in an uploads folder, an image directory or a static site with no application on it has no innocent explanation. Where a file sits often says more than what it’s called — which is also why the next post tells you to copy things rather than move them.

And it sets up the single most useful cross-check in the whole investigation. The log records when a file was requested. The filesystem records when it was written. Two independent sources, neither aware of the other.

  • If they agree, you have a finding rather than a claim.
  • If the file’s date is earlier than any request that could have created it, the date is a lie — and you’ve just caught backdating without needing to be clever about it.

So the thing you take through the tape is short and specific: exact paths, exact times, and the addresses that touched them. Everything in the next post is checking that list.

What you can do yourself, and what you can’t

Downloading the logs is yours, and it’s the urgent part. cPanel makes it a couple of clicks, and opening a retention ticket needs no skill at all. If you do nothing else after reading this, do that today.

Reading them properly is where most owners hand off — and that’s fine, provided you understand what’s being done, because “did you pull the logs before you started?” is the single best question to ask anyone you hire. If the answer is no, the answer to how you were compromised will be a guess.


Back: What Google Search Console Shows After a Hack

Logs are the only witness that was present the whole time. If yours run to hundreds of thousands of lines and you want them read rather than skimmed, going through them line by line is the work.

Start a Recovery ReviewPaid assessment · scope agreed up front

Next: Observing the Scene → — the fast pass, why the capture is the one thing you rush, and why they must not know you’re looking.

Hub: Invasion Investigation Blog Series


ProVAE builds and recovers websites in Douglas, Georgia, serving South Georgia.