A small business website is rarely attacked by someone who knows what the business does. It is attacked by a bot that works its way through millions of addresses, checking for known vulnerabilities in popular plugins, configuration files left on the server and passwords from other people's leaks. Business website security therefore does not depend on whether the company is "interesting enough", but on whether the site passes the same test the bot runs against every site.
Below we go through seven areas: updates, backups, HTTPS, security headers, forms, account access and server configuration. For each one we say what should be done and how to check it without asking your developer. At the end there is a plan for the first hours after a break-in, including your obligations under the GDPR.
- Updates matter most: the system, plugins, themes and the PHP version. PHP 8.1 no longer receives security fixes, and 8.2 only receives them until the end of 2026.
- A backup has to sit off the server and out of reach of anyone who takes over the server, and it has to go back further than the moment of the break-in.
- From 15 March 2026 a TLS certificate can be valid for at most 200 days, and from 2029 for 47 days. Renewal has to be automatic and monitored.
- A few HTTP headers shut down entire classes of attack. You can check them in a minute with MDN HTTP Observatory.
- After a break-in, preserve the evidence and change every password first, then clean up. If personal data has leaked, as a rule you have 72 hours to notify the President of UODO, Poland's data protection authority.
Who attacks a business website and what they are looking for
To an attacker, a compromised business website is an asset: it has a domain with a history, the trust of search engines and a server that has already been paid for. Hundreds of hidden SEO spam pages appear on the site, visitors from Google results are redirected to scams, a fake bank login page lands in a subdirectory, in an online store a script copies card details from the payment form, and the server sends spam or mines cryptocurrency.
The owner is usually the last to find out, because the redirects are often set up only for traffic from search engines or from phones, while someone typing the address by hand on an office computer sees the normal site. The first sign is often a message from a customer, the hosting account being suspended or a warning from Google. Sites with a detected problem can get a warning label in search results or a full-screen warning in the browser, which stops traffic overnight.
This shows up clearly in the OWASP Top 10:2025, where the top three places go to broken access control, security misconfiguration and software supply chain failures, meaning problems with dependencies and components that come from others. On a business website this translates into accounts with overly broad permissions, files made public by mistake and plugins that nobody updates.
Updates: the system, plugins and PHP version
Updates are the item that removes the biggest share of the risk for the smallest cost. In the WordPress ecosystem, which a large share of business websites run on, Patchstack's report for 2025 attributes 91% of the 11,334 new vulnerabilities to plugins and only 6 to core. We covered this in more detail in our comparison of WordPress and headless CMS. The same report shows something less obvious: for 46% of the vulnerabilities there was no fix at the time of disclosure. In that case the "update" button does not help; what helps is disabling the plugin or adding a firewall rule, which means someone has to read the vulnerability announcements.
The second layer of updates is the server environment. Each PHP version gets two years of active support and then two years of security fixes only, after which it stops being patched. According to the official PHP schedule the situation is as follows:
PHP version support
| Version | Security fixes until | Status in September 2026 |
|---|---|---|
| 8.1 and older | 31 December 2025 | Unsupported, update urgently |
| 8.2 | 31 December 2026 | Security fixes only, move off it this year |
| 8.3 | 31 December 2027 | Security fixes only |
| 8.4 | 31 December 2028 | Full support |
| 8.5 | 31 December 2029 | Full support |
You can check your PHP version in the hosting panel. Changing it is usually a single click, but an old plugin or theme may stop working on the new version, which is why you test the site on a staging copy first.
The third layer is dependencies outside the CMS: JavaScript libraries in more modern projects, the system image on a VPS, the hosting panel. How to review and update dependencies in a project after launch is described in our post on monitoring and maintenance.
There is also a category almost everyone forgets: old copies of the site. An /old-site/ directory left after a move, a test. subdomain with a version from two years ago, a WordPress installation set up "for a moment" to try out a theme. Nobody updates them because nobody remembers them, and a break-in through one of them gives access to the same server as the main site. If you are not using something, delete it rather than hide it.
Backups that survive a break-in
A backup protects you against disk failure, a botched update and an editor's mistake. It only protects you against a break-in if it meets three additional conditions that people usually do not think about.
First: the attacker cannot delete it. CISA's ransomware guide explicitly warns that malware looks for accessible backups and deletes or encrypts them. A backup on the same server, in a directory next to the site, disappears together with the site. So does a backup in external storage to which the server has full write and delete rights, because the credentials are stored on the server. The server should be able to add backups but not delete them, and old backups should be removed on the storage side, according to rules set from a different account.
Second: the backup has to go back further than the break-in. Weeks can pass between a site being taken over and the takeover being detected. If you keep only seven daily backups, all of them may already contain the infected site. A sensible arrangement is daily backups covering the last few weeks, plus weekly or monthly ones going back several months.
Third: your hosting provider's backup is not the same as your own backup. It is convenient, but it sits in the same infrastructure and is accessible from the same account. If the hosting panel account is taken over, the attacker has access to both the site and its backups.
We described the 3-2-1 rule and the database restore test in our post on maintenance after launch. You need to practise restoring before you need it, because after a break-in there is no time to learn how the backup tool works.
HTTPS and a certificate that renews itself
A TLS certificate is standard today, but the problems with certificates have not gone away; their cause has changed. A certificate is no longer bought once a year but renewed automatically, and automation can quietly stop working.
Certificate lifetimes are getting shorter according to the schedule adopted by the CA/Browser Forum in ballot SC-081 in April 2025. From 15 March 2026 a publicly trusted TLS certificate can be valid for at most 200 days, from 15 March 2027 for 100 days, and from 15 March 2029 for 47 days. Renewing certificates by hand stops being a realistic option.
Let's Encrypt, the free certificate authority, issues certificates valid for 90 days and is shortening them further: from 13 May 2026 the tlsserver profile issues 45-day certificates, and the default profile will move to 64 days on 10 February 2027 and to 45 days on 16 February 2028. Another change matters more to a site owner: since June 2025 Let's Encrypt no longer sends emails about expiring certificates. If renewal stops working, the first sign will be a warning in your customer's browser, unless you have monitoring of your own.
You can check a certificate's expiry date from the terminal with a single command (replace example.com with your domain):
The output takes the form notAfter= followed by a date. Let's Encrypt recommends renewing after about two thirds of the validity period, so if clearly less than a third of that period is left before expiry, renewal is probably not working. It is worth plugging the same check into monitoring that will warn you in advance.
HTTPS on its own is not everything. A visit via http:// should end in a 301 redirect to the encrypted version, and the site should not load any resources over HTTP, because the browser will block them or mark the page as not secure. The next step is the HSTS header, described below.
Security headers: a few lines of configuration
HTTP headers are instructions the server sends to the browser along with the page: "connect to me only over HTTPS", "do not embed me in a frame", "do not run scripts from outside this list". They do not fix bugs in the code, but they make those bugs harder to exploit.
Headers worth starting with
| Header | What it protects against | Sensible starting point |
|---|---|---|
| Strict-Transport-Security (HSTS) | Connections over unencrypted HTTP and eavesdropping on public networks | max-age=31536000; includeSubDomains |
| Content-Security-Policy | Injected scripts (XSS), embedding the site in frames | Report-Only mode first, then a list of allowed sources |
| X-Content-Type-Options | Files being interpreted as a different type, for example an image as a script | nosniff |
| Referrer-Policy | Sending full page URLs to other services | strict-origin-when-cross-origin |
| Permissions-Policy | Scripts accessing the camera, microphone and location | camera=(), microphone=(), geolocation=() |
| X-Frame-Options | Embedding the site in a frame in older browsers | SAMEORIGIN |
Two notes on the table. HSTS only takes effect after the first visit over HTTPS, because browsers ignore this header when it is sent over HTTP. The first-visit gap is closed by adding the domain to the HSTS preload list, but that requires HTTPS on every subdomain, and getting removed from the list is slow and difficult. Submit your domain only when you are sure that no subdomain, including those for email or an old online store, needs HTTP.
The second note concerns a header that is not in the table. X-XSS-Protection still appears in guides, but MDN marks it as deprecated and warns that in some cases it can create vulnerabilities of its own. Content-Security-Policy is used instead.
This is what an example nginx configuration looks like:
The always parameter makes nginx add the header to error pages as well, not only to responses with success and redirect codes. There is also a trap that regularly leaves sites without their headers: according to the nginx documentation, add_header directives are inherited from the previous configuration level only if none are defined on the current level. All it takes is for a location block for static files to add a Cache-Control header, and every security header from the server level disappears for those files. The fix is a shared file pulled in with include in every block that has its own add_header.
Content-Security-Policy takes the most work, because it has to list every source of scripts, styles, fonts and frames the site uses, including analytics and marketing tools. That is why you start with the Content-Security-Policy-Report-Only header: the browser blocks nothing and only reports violations in the console and, with the report-to directive, to a specified address as well. After a few days the list of violations shows what the policy is missing, and you can switch it to blocking mode.
You can check the result in MDN HTTP Observatory: enter the address and you get a grade and a list of missing headers, with an explanation of each one. On shared hosting without access to the nginx configuration, some headers are set in the .htaccess file or in the hosting panel.
Forms: spam, abuse and email
Through a contact form, bots send spam, test whether the form can be used to send email to arbitrary addresses, and try to upload files wherever the form accepts attachments.
Spam protection is best built in layers, starting with the ones least annoying for humans:
- Hidden field (honeypot). A field invisible to people that bots fill in, because they fill in everything. A message with that field filled in is rejected.
- Minimum completion time. A form submitted two seconds after the page loaded was not filled in by a human.
- A limit on submissions. Several messages from one IP address in a short time are a signal to block it.
- Server-side validation. Checking fields in the browser is there for the user's convenience, not for security, because a bot sends its data without going through the form.
- CAPTCHA as the last layer. Cloudflare Turnstile works without showing puzzles and does not require routing the site's traffic through Cloudflare. Google's reCAPTCHA, on the free Essentials tier, covers up to 10,000 assessments per month per organisation, and once that limit is exceeded without billing enabled it returns an error. The form code needs a planned behaviour for that situation, otherwise towards the end of the month the form will stop working or start letting everything through.
The CAPTCHA service's script processes visitor data on the provider's side, so it should be described in the site's privacy policy.
The second group of problems concerns sending email. A form that inserts the address entered by the user into the message headers without checking it lets someone add extra recipients and use the site as a spam gateway. The sender address should always be an address on your own domain, and the user's address can at most go into the "reply-to" field, after validation. Proven mail libraries such as PHPMailer, which WordPress among others uses to send email, validate addresses and protect against header injection, so it is better to use them than to assemble the message by hand.
Mail sent from a form also has to get past the recipient's filters. Since 1 February 2024 Gmail has required all senders to authenticate with SPF or DKIM, and those sending more than 5,000 messages a day to have DMARC as well. SPF, DKIM and DMARC records also protect your domain from being spoofed in phishing emails to your customers. Without them, messages sent from the form may land in spam or be rejected.
If the form accepts attachments, restrict the file types and sizes, check the type by content rather than by extension, and save the files outside the public directory.
Access to the panel, hosting and domain
The simplest way to take over a site does not require any vulnerability at all; a password is enough.
Everyone should have their own account, not a shared "admin" login with a password passed around by email. That lets you grant permissions according to need, see who changed what and revoke one person's access. WordPress has roles for this: an editor publishes and edits content but does not install plugins or change settings. One or two people should have an administrator account.
The most neglected task is revoking access. A former employee, the previous agency, a freelancer who fixed the form two years ago: each of those accounts is an open door, and the older the account, the more likely it is that its password appears in some leak.
Passwords should be long, not complicated. The August 2025 version of NIST SP 800-63B requires at least 15 characters for a password used as the only authentication factor, prohibits imposing character composition rules and periodic password changes without reason, and requires checking passwords against a list of known and breached ones. In practice that means a password manager, a unique password for every service and two-factor authentication wherever it is available.
For WordPress, the official hardening guide also recommends disabling file editing from the dashboard with the DISALLOW_FILE_EDIT constant, adding server-side protection for the /wp-admin/ directory and limiting the database user's privileges to reading and writing data.
The site panel is not the most important account, though. Above it sit the ones that let someone take over everything at once:
- Domain registrar. Whoever takes over the domain can point the site and the email wherever they like.
- DNS. Changing a single record is enough to send traffic to someone else's server.
- Hosting panel or cloud account. Full access to the files, the database and the backups.
- Business email. Through password resets it gives access to every other account.
- Code repository, if the site is deployed from it automatically.
Each of these accounts should be registered to the company, not to the developer, and should have two-factor authentication and a short, known list of people with access. We also wrote about why the domain and the accounts have to be yours in our post on how to read a software house quote.
Server configuration: files nobody should see
The misconfiguration category on the OWASP list takes a very concrete form on business websites: files that ended up in the public directory by mistake. A .git directory with the full code history, sometimes with passwords saved in old versions. An .env file with database credentials and API keys. A backup.zip archive or a database.sql database dump left behind after a move. A phpinfo.php file once added for diagnostics. A wp-config.php.bak copy made during a manual edit.
Bots check these paths on every site they come across. You can do the same:
Every path should return 404 or 403. A 200 means the file is available to anyone, although some servers respond to non-existent addresses with a 200 and their own error page. If you see 200, open the address in a browser and check what is actually displayed.
The same list includes directory listing, meaning the server showing a list of the files in a folder that has no index page, and error messages that reveal full file paths and fragments of database queries. Both are switched off in the server and application configuration.
It is also worth adding a security.txt file as described in RFC 9116. It lives at /.well-known/security.txt and tells people who have found a vulnerability on your site how to report it. Two fields are required, and the RFC recommends setting the date in the Expires field less than a year ahead:
Monitoring: be the first to know
The longer a break-in goes unnoticed, the more it costs. A few free tools let you find out about it before your customers do.
Google Search Console has a Security issues report that tells you about detected hacking, malware and social engineering. It only works if the domain has been added to Search Console and the notifications go to an address someone actually reads, not to the inbox of the developer from three years ago.
External uptime monitoring should check not only the response code but also the presence of specific text on the page, because a compromised site often responds correctly and simply shows something else. Add to that the certificate monitoring described above, and a notification for every new administrator account in the CMS.
What to do after a break-in: the first hours
The first instinct after discovering a break-in is to clean up: delete the suspicious files, restore a backup and forget about it. That is a mistake, because if you do not know how the attacker got in, they can come back the same way. The order matters:
Before you delete anything, make a copy of the current state: the files, the database and the server logs. Logs are rotated and may disappear within a few days, and they are the only thing that shows how the attack came in.
If the site is spreading malware or redirecting customers to scams, switch on a maintenance page or block the traffic. Notify your hosting provider, because the compromised server may have been attacking others too.
From a clean device: CMS accounts, hosting panel, FTP, SSH, the database password, API keys for payments and email sending, the domain registrar, email. In WordPress, also generate new keys and salts in wp-config.php, which logs out every session.
Review the logs for unusual requests to PHP files, check file modification dates, and check the plugin list for known vulnerabilities. Without this step the clean-up is only temporary.
Restore a backup from before the break-in, or reinstall the system and plugins from official sources and carry over only verified content. Manually cleaning individual files rarely finds everything.
New administrator accounts, scheduled tasks, PHP files in the images directory, changes to .htaccess, redirects that only fire for traffic from Google or from phones.
Update or remove the vulnerable component, then restore the site. In Search Console, use the "Request review" button and describe what you did. According to Google, the review takes from several days to several weeks.
With an online store or a site with customer accounts, it is worth bringing in someone who has done this before straight away. You can also report the incident to CERT Polska, Poland's national computer emergency response team, at incydent.cert.pl.
A personal data breach: your GDPR obligations
A contact form, customer accounts, orders, a newsletter subscriber list: almost every business website processes personal data, and the business is the controller of that data. A break-in in which the attacker could read, change or delete that data is a personal data breach within the meaning of the GDPR.
Under Article 33 of the GDPR, the controller notifies the breach to the supervisory authority, which in Poland is the President of UODO (the Personal Data Protection Office), without undue delay and, where feasible, not later than 72 hours after becoming aware of it. No notification is needed if the breach is unlikely to result in a risk to the rights and freedoms of the people the data relates to. The controller documents every breach, including those that are not notified. If the risk is high, Article 34 also requires informing the people whose data has leaked.
The 72 hours run from the moment you become aware of the breach, not from when it occurred. A late notification is possible, but it has to include the reasons for the delay. UODO accepts notifications electronically through a form on biznes.gov.pl, the Polish government's business portal. If the site is maintained by an external company that processes data on your behalf, that company must notify you without undue delay once it becomes aware of a breach, but notifying UODO remains your obligation.
Article 32, in turn, requires security measures appropriate to the risk, including the ability to restore the availability of data quickly after an incident and to test the security measures regularly. A tested backup and an up-to-date system are therefore also a legal requirement. This section is not a substitute for legal advice, and if there is a serious leak it is worth getting that advice immediately.
Business website security checklist for one afternoon
You can check most of the points in this post yourself, without access to the code. The table lists them in order of importance:
Website security checklist
| What to check | How | How often |
|---|---|---|
| PHP version and updates to the CMS and plugins | Hosting panel and CMS panel | Monthly |
| Unused plugins, themes and old copies of the site | Plugin list, directories and subdomains on the server | Quarterly |
| Off-server backup | Date of the latest backup and a test restore | Test restore quarterly |
| Certificate expiry date | The openssl command or monitoring | Automatically, daily |
| Redirect from HTTP to HTTPS | Visit the address with http:// | After every server change |
| Security headers | MDN HTTP Observatory | After every configuration change |
| Publicly visible files | The curl script | Quarterly |
| List of accounts with access to the CMS, hosting, domain and DNS | Review of the accounts in each service | Quarterly and with every team change |
| Two-factor authentication on key accounts | Security settings of each account | Once, then for every new account |
| SPF, DKIM and DMARC for the domain | Headers of a form message in the recipient's inbox | After every email change |
| Search Console notifications | Who receives them and whether they read them | Once a year |
Frequently asked questions
Can a small business website really get hacked?
Yes. Bots do not pick businesses; they check every address in turn for known vulnerabilities, and small sites are updated less often. What matters to an attacker is the domain and the server, not the number of visits.
How can I check whether my website has been hacked?
Check the Security issues report in Google Search Console, search Google for site:yourdomain.com and look for pages you did not create, open the site on a phone through a search result rather than by typing the address, and review the list of administrator accounts in the CMS. An unknown administrator account is an almost certain sign of a break-in.
Is a free SSL certificate worse than a paid one?
Not in terms of encryption, which is the same. Paid OV and EV certificates also verify the company's details, but modern browsers do not highlight them in the address bar. For a typical business website, a free certificate with automatic renewal is the right choice.
Is a security plugin enough?
No. A plugin can limit login attempts, scan files and block some known attacks, but it will not make an off-server backup, update PHP, revoke a former employee's access or secure your account at the domain registrar. It is one of the layers, not a replacement for the others.
Do I always have to report a break-in to UODO, the data protection authority?
Not always. The obligation does not arise if the breach is unlikely to result in a risk to people's rights or freedoms, for example when the attacker had no access to any personal data. The decision and its justification must still be documented, though, and if in doubt it is better to consult a lawyer within those 72 hours, not after them.
Start with the list, not the tool
Business website security rarely requires expensive software, but it does require a person who is responsible for the checklist in this post. If there is no such person in the company, the most important decision is to settle who it will be: an employee, the site's developer or a maintenance company.
If you would like us to go through this list on your site, or to plan the security of a new project from the start, write to us through the contact form. Quotes are free. Our approach to code quality can also be seen in the open source projects we contribute to, including PHPMailer; we describe them on our open source page. You will find the scope of our services on the offer page.



