- Spam pages in Google, unknown URLs in a
site:search or a Search Console security warning mean your OJS site has been compromised. Act quickly. - Back up the infected site for evidence, change every password, then find injected files by comparing your install with a clean copy of the same OJS version.
- Remove the cause, not just the spam: update OJS, block PHP execution in
public/, fix permissions, then request a review in Search Console.
Signs your OJS site is hacked
An OJS hack rarely looks like a defaced homepage. The most common attack today is SEO spam: hidden pages or links that promote gambling ("slot gacor", "judi online", togel, casino), pharmacy or counterfeit products. Attackers use your journal's trusted academic domain to rank them. Your journal may look completely normal to you while Google sees something very different. Common signs:
- Strange titles in Google. Searching for your journal shows results with gambling or pharmacy titles and descriptions, sometimes in Indonesian, Japanese or other languages your journal doesn't publish in.
- Unknown URLs in a
site:search. Searchingsite:yourjournal.orglists pages nobody at the journal created, often in odd folders or with long random file names. - Search Console warnings. The Security issues report flags hacked content, or the Pages report shows a sudden jump in indexed URLs. The Performance report shows gambling search queries.
- Cloaked redirects. Visitors on mobile, or arriving from Google, get redirected to a gambling site, while you see the normal page when you type the address directly.
- Server-side clues. Unknown administrator or journal manager accounts, new files you didn't upload, a warning from your host about malware or outgoing spam, or unexplained CPU spikes.
Check what Googlebot sees. In Search Console, use URL Inspection → Test live URL → View tested page and look at the HTML. From a terminal, you can request a page the way a crawler or a visitor from Google would and look for spam keywords or redirects:
# Fetch the homepage with a Googlebot user agent and search for spam keywords
curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://journal.example.org/ | grep -ioE "slot gacor|judi online|togel|casino" | sort | uniq -c
# Check whether a mobile visitor arriving from Google is redirected elsewhere
curl -sI -e "https://www.google.com/" \
-A "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 Mobile" https://journal.example.org/
A 301 or 302 to an unknown domain in the second result, or spam keywords in the first, confirms cloaking.
How attackers get into OJS sites
Knowing the entry point matters, because cleaning without closing it means you will be reinfected within days. The usual causes, in general terms:
- Outdated OJS. PKP fixes security issues in point releases. Sites still on OJS 2.x or early 3.x releases miss years of fixes.
- Outdated or untrusted plugins and themes. Abandoned plugins, and themes downloaded from unofficial sources, can contain vulnerabilities or ready-made backdoors.
- Weak, reused or stolen passwords. These include passwords for the OJS administrator or journal managers, and for cPanel, FTP, SSH or the database. Password-stealing malware on one editor's computer is enough.
- Other applications on the same hosting account. An old WordPress site, a forgotten test copy of OJS or an outdated tool in a neighbouring folder can be the way in. Once attackers are on the account, they can write to every site on it.
- Writable folders that execute PHP. If an uploaded file in
public/or in a files directory inside the web root can run as PHP, any upload weakness becomes a full compromise. - Exposed backups and configuration copies. Files like
config.inc.php.bak,.sqldumps or.ziparchives left in the web root can be downloaded, revealing database passwords.
First response: contain the damage
- Take a forensic backup first
Before changing anything, copy the full site, the files directory, a database dump and the web server access logs to a separate location, and label it as infected. You need it to find the entry point and timeline. Never restore from it blindly.
- Change every password from a clean computer
Change the hosting/cPanel, FTP/SFTP, SSH and database passwords (then update the database password in
config.inc.php). Also change the passwords for the OJS site administrator, every journal manager and editor, and the SMTP mailbox. Consider rotating credentials stored in OJS, such as Crossref deposit credentials. Remove SSH keys you don't recognise. - Check for rogue accounts
Look for administrator or manager accounts you didn't create (a query is shown in the database section below). Remove their roles and disable them immediately.
- Look for persistence
Check the account's cron jobs (
crontab -lor cPanel Cron Jobs) for unknown entries that re-download malware. Also check for unknown FTP accounts. - Tell your host
Hosting providers often have logs and malware scanners you can't access, and they may already have flagged the account.
If the site is redirecting visitors to malicious pages, consider showing a temporary maintenance page while you work. Need hands-on help right now? Our OJS technical support team handles emergency hacked-site cleanups.
Find malicious files and backdoors in OJS
Run these read-only commands over SSH. Replace /var/www/ojs with your OJS folder and /var/www/ojs-files with your files directory (files_dir in config.inc.php).
# 1. Files changed in the last 30 days, newest first (ctime is harder to fake than mtime)
find /var/www/ojs /var/www/ojs-files -type f -ctime -30 -not -path "*/cache/*" \
-printf '%CY-%Cm-%Cd %CH:%CM %p\n' | sort -r | head -200
# 2. Scripts where none should exist: public/ and the files directory
find /var/www/ojs/public /var/www/ojs-files -type f -regextype posix-extended \
-iregex '.*\.(php[0-9]?|phtml|phar|pl|py|cgi|sh)$'
# 3. HTML pages dropped into public/
find /var/www/ojs/public -type f -iname "*.htm*"
# 4. Every .htaccess and .user.ini file
find /var/www/ojs /var/www/ojs-files \( -name ".htaccess" -o -name ".user.ini" \)
# 5. Typical obfuscation patterns in PHP files
grep -rlE --include='*.php' \
'eval\(base64_decode|eval\(gzinflate|gzinflate\(base64_decode|str_rot13\(|eval\(\$_(POST|GET|REQUEST)' /var/www/ojs
# 6. Spam keywords in code, templates and config files
grep -rliE 'slot gacor|judi online|togel|casino' /var/www/ojs \
--include='*.php' --include='*.tpl' --include='*.js' --include='*.htm*' --include='.htaccess'
How to read the results:
public/should hold only images, stylesheets and other uploaded media. Any.phpfile there, or in the files directory, is almost certainly malicious. So are HTML pages you didn't upload and image-like names such aslogo.jpg.php.- Suspicious
.htaccessrules includeRewriteCondlines that testHTTP_USER_AGENTfor googlebot orHTTP_REFERERfor google before redirecting. Also look forAddHandlerorAddTypelines that make.jpgor.txtfiles run as PHP, andauto_prepend_filedirectives (also in.user.ini). - Pattern hits are leads, not proof. Some legitimate libraries use
base64_decode. Check each hit, which is why the comparison in the next step is the most reliable test.
Compare core files with a clean copy of the same OJS version
The most reliable way to find modified or added files is to compare your installation with a fresh download of exactly the same release. Find your version under Administration → System Information. Then download that release from the official PKP download page (older releases are available there too) and compare:
cd /tmp
wget https://pkp.sfu.ca/ojs/download/ojs-3.4.0-11.tar.gz # use YOUR exact version
tar -xzf ojs-3.4.0-11.tar.gz
# List differing and extra files; skip cache, public and your config
diff -rq /tmp/ojs-3.4.0-11 /var/www/ojs -x cache -x public -x config.inc.php
Two kinds of output matter. Files … differ means a core file was changed. Unless you knowingly patched it, treat it as infected. Only in /var/www/ojs/… means a file exists on your server that isn't in the official package. That is expected for third-party plugins you installed and suspicious for everything else, especially PHP files in lib/, classes/, plugins/ or templates/. Review public/ separately using the checks above.
Check the database for injected content and users
Spam does not always live in files. Attackers with a stolen journal manager login, or database access, can place links and scripts in journal settings, the page footer, static pages, announcements, custom blocks and navigation menus. They can even create fake issues and articles. A quick way to search everything at once is to grep a database dump:
mysqldump --single-transaction -u ojs_user -p ojs_db > /root/ojs-check.sql
# Count spam keywords and injected script/iframe tags
grep -oiE 'slot gacor|judi online|togel|casino|<iframe[^>]*>|<script[^>]*src=[^>]*>' /root/ojs-check.sql \
| sort | uniq -c | sort -rn | head -50
# Show which lines (and therefore which INSERT INTO table) contain a keyword
grep -n -oiE '.{0,80}slot gacor.{0,80}' /root/ojs-check.sql | head -20
Expect some legitimate <script> hits, for example analytics code you added yourself. To check for rogue privileged accounts, run this read-only query (OJS 3.x):
SELECT u.user_id, u.username, u.email, u.date_registered, u.date_last_login, ug.role_id
FROM users u
JOIN user_user_groups uug ON uug.user_id = u.user_id
JOIN user_groups ug ON ug.user_group_id = uug.user_group_id
WHERE ug.role_id IN (1, 16) -- 1 = Site Administrator, 16 = manager-level roles
ORDER BY u.date_registered DESC;
Fix what you find through the OJS interface where possible: Settings → Website (appearance, footer, static pages, navigation menus, plugins), announcements, and unpublishing any fake issues or articles. Only edit the database directly if you are experienced, and only after a fresh backup.
Clean and rebuild the OJS site
Deleting the spam pages you can see is not enough. Backdoors are designed to survive that. The safest approach is to rebuild from known-good code:
- Prepare clean code
Extract a fresh copy of OJS into a new folder. Ideally use the latest release of your branch, but if you plan a bigger upgrade, clean first on your current version.
- Carry over only what you must, after inspection
Bring over
config.inc.phpafter checking it contains no PHP code beyond the standard first line. Bringpublic/after removing scripts and pages you didn't create, and the files directory after scanning it. Reinstall third-party plugins and themes from their official sources rather than copying the old folders. - Swap and reset
Point the web root at the clean folder and set correct permissions (below). Empty the contents of
cache/, since cached templates can be poisoned too; OJS rebuilds them. - Clean the database
Remove injected content and rogue accounts as described above.
- Remove the spam URLs
Make sure every injected URL now returns
404or410(see the Google section). - Re-scan after 24–48 hours
Run the file checks again. If spam files come back, a backdoor remains, a cron job is reinstalling it, another site on the account is infected or a password is still compromised.
Check the whole hosting account
If other websites share the same hosting account, scan them too. One infected neighbouring site will keep re-infecting a perfectly cleaned OJS installation.
Harden OJS security so it doesn't happen again
1. Update. Run a supported, current release. At the time of writing, OJS 3.5 is PKP's current long-term support (LTS) branch and 3.3 the older LTS branch. Keep plugins current and remove ones you don't use. If your journal is several versions behind, our OJS upgrade and migration service can bring it up to date safely.
2. Block PHP execution where uploads live. OJS never needs to run scripts from public/. On Apache 2.4, add an .htaccess file inside public/:
<FilesMatch "\.(php[0-9]?|phtml|phar|pl|py|cgi|sh)$">
Require all denied
</FilesMatch>
If the files directory has to stay inside the web root for now, place a file containing just Require all denied in it, and plan to move it out. If public images stop loading with a 500 error after adding the file, your host doesn't allow these directives in .htaccess. Put the rule in the virtual host configuration or ask your host. On Nginx, the equivalent goes above your main PHP location block:
location ~* ^/public/.*\.(php[0-9]?|phtml|phar)$ {
deny all;
}
3. Correct permissions. As PKP's documentation recommends, the web server should be able to write only to cache/, public/ and the files directory. Leave config.inc.php read-only after installation, and never use chmod 777.
4. Files directory outside the web root. This keeps unpublished manuscripts and reviewer files away from direct download.
5. Tighten the configuration. Set allowed_hosts to your journal's hostnames, keep display_errors and show_stacktrace off, and set long random values for salt and api_key_secret. If users don't need to upload images to their profiles, consider public_user_dir_size = 0.
6. Accounts and access. Keep administrator and journal manager accounts to a minimum, remove former staff, and use SFTP instead of FTP. Turn on two-factor authentication for your hosting panel.
7. WAF, monitoring and backups. Use your host's or CDN's web application firewall. Enable Search Console email alerts, and use file-change monitoring or your host's malware scanner. Keep automated off-site backups of the database, files directory, public/ and config.inc.php. PKP suggests keeping daily backups for a week, weekly for a month and monthly for a year, because infections are often discovered late.
Much of this depends on the hosting environment. Our guide to choosing OJS hosting explains what to look for: account isolation, backups, malware scanning and PHP version control.
Recover your journal in Google Search
- Make spam URLs return 404 or 410
Deleted files normally return 404. Check a sample with
curl -I. If a spam URL still returns 200 because of a rewrite rule, remove the rule, or for a whole injected folder add something likeRedirectMatch 410 ^/spam-folder/to the root.htaccess. - Check Search Console ownership
Under Settings → Users and permissions, remove any owners you don't recognise and delete their verification files (
google….html) or DNS records. Attackers sometimes verify hacked sites to submit their own sitemaps. - Request a review
If the Security issues report lists problems, fix them everywhere, then click Request Review and describe what you did (removed injected files, updated OJS, changed passwords). Do the same in the Manual actions report if anything is listed there. Reviews of hacked-spam cases can take from days to several weeks.
- Hide the worst URLs temporarily
The Removals tool can hide spam URLs or a URL prefix from results. This is temporary, which is why step 1 matters.
- Confirm the cloaking is gone
Use URL Inspection → Test live URL on your homepage and a few article pages, and repeat the
curlchecks from the first section. - Resubmit your sitemap
Submit the real OJS sitemap (see how to find your OJS sitemap) and remove any sitemap entries you didn't add. If you use Bing Webmaster Tools, repeat these steps there.
Google's spam policies describe how hacked content is treated. Spam results normally fade over the following weeks as pages are recrawled. Keep checking site: results until they are clean.
When to get professional help
Bring in an expert if spam keeps coming back after cleaning, you have no SSH access to run the checks, the site holds unpublished manuscripts or personal data, or the journal is under evaluation by an indexing database. OJS Academy has 10+ years of experience with OJS. We do this work defensively and methodically: forensic backup, root-cause analysis, clean rebuild, hardening and Search Console recovery. Request emergency OJS support or reach us through the contact page.
If your problem is fake user accounts rather than injected pages, see our separate guide on stopping OJS spam registrations.
Frequently Asked Questions
Why do hackers target OJS journal websites?
Mostly for search rankings. University and journal domains are trusted by search engines, so spam pages placed on them rank well for gambling, casino and pharmacy keywords. Attackers scan the web for outdated software, weak passwords and writable folders, and OJS sites that are rarely updated are easy to find.
What does "slot gacor" or "judi online" spam on my journal mean?
These are gambling terms used in large SEO spam campaigns. If they appear in Google results for your domain, someone has placed pages, files or database content on your site, often hidden so that only Googlebot or visitors arriving from Google see them. Treat it as a security incident: find and remove the injected content, close the entry point, then ask Google to re-check the site.
Can I just restore an old backup?
Only if you are sure the backup was made before the compromise, and even then you must update OJS and close the entry point, or the same attack will succeed again. Sites are often compromised weeks before the spam becomes visible, so a recent backup may already contain the backdoor. Restore to a separate folder and scan it first.
Will upgrading OJS remove the hack?
Not by itself. An upgrade replaces core code and closes known vulnerabilities, but backdoors in public/, the files directory, third-party plugins, the database or other sites on the same account survive it. Clean first, then upgrade. Our OJS upgrade service can do both safely.
How long until the spam pages disappear from Google?
Once the pages return 404 or 410 and any security issue review is approved, spam URLs drop out as Google recrawls them. That usually takes days to several weeks. The Search Console Removals tool can hide URLs sooner, but only temporarily, so the pages must really be gone.
How do I find out which OJS version I am running?
Log in as site administrator and open Administration → System Information. The current version is listed at the top. You need the exact version (for example 3.4.0-11) to download a matching clean copy for comparison.
Do I need to tell our users about the hack?
If there is any sign that user data (names, emails, password hashes or unpublished manuscripts) was accessed, talk to your institution's IT or data protection officer. Data protection laws in many countries set notification rules. Either way, ask all editors and administrators to change their passwords.