Documentation Index
Fetch the complete documentation index at: https://mintlify.com/joaquinobed/simple-invoice/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Deploying Simple Invoice to production requires careful attention to security, performance, and reliability. This guide covers essential configuration and hardening steps.
Pre-Deployment Checklist
Before deploying to production:
Security Configuration
1. Database Credentials
Critical: The default configuration uses root with an empty password. This must be changed before production deployment.
Update config/db.php with secure credentials:
<?php
/*Datos de conexion a la base de datos*/
define('DB_HOST', 'localhost');
define('DB_USER', 'invoice_user'); // Change from 'root'
define('DB_PASS', 'strong_secure_password_here'); // Set a strong password
define('DB_NAME', 'simple_invoice');
?>
Password requirements:
- Minimum 16 characters
- Mix of uppercase, lowercase, numbers, and symbols
- No dictionary words
- Unique to this application
2. File Permissions
Set restrictive permissions on sensitive files:
# Configuration files - read-only for web server
chmod 640 config/db.php
chmod 640 config/conexion.php
chown www-data:www-data config/*.php
# Upload directories - writable
chmod 755 img/
chmod 755 pdf/documentos/
chmod 755 pdf/documentos/res/
# Application files - read-only
find . -type f -name "*.php" ! -path "./config/*" ! -path "./img/*" ! -path "./pdf/documentos/*" -exec chmod 644 {} \;
The application requires write access to /img for logo uploads and /pdf/documentos for invoice PDF generation.
3. Database Security
Create dedicated database user
CREATE USER 'invoice_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON simple_invoice.* TO 'invoice_user'@'localhost';
FLUSH PRIVILEGES;
Import database schema
mysql -u invoice_user -p simple_invoice < simple_invoice.sql
Remove root access
-- Verify tables were created
USE simple_invoice;
SHOW TABLES;
-- Tables: clientes, currencies, detalle_factura, facturas,
-- perfil, productos, users
Secure MySQL configuration
Add to /etc/mysql/my.cnf:[mysqld]
local-infile=0
skip-show-database
bind-address=127.0.0.1
4. SSL/HTTPS Setup
Required for production: Always use HTTPS to protect user credentials and sensitive invoice data.
Apache Configuration
<VirtualHost *:443>
ServerName invoice.yourdomain.com
DocumentRoot /var/www/simple-invoice
SSLEngine on
SSLCertificateFile /etc/ssl/certs/invoice.crt
SSLCertificateKeyFile /etc/ssl/private/invoice.key
SSLCertificateChainFile /etc/ssl/certs/chain.pem
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
<Directory /var/www/simple-invoice>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# Redirect HTTP to HTTPS
<VirtualHost *:80>
ServerName invoice.yourdomain.com
Redirect permanent / https://invoice.yourdomain.com/
</VirtualHost>
Nginx Configuration
server {
listen 443 ssl http2;
server_name invoice.yourdomain.com;
root /var/www/simple-invoice;
index index.php;
ssl_certificate /etc/ssl/certs/invoice.crt;
ssl_certificate_key /etc/ssl/private/invoice.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to sensitive files
location ~ /config/ {
deny all;
return 404;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name invoice.yourdomain.com;
return 301 https://$server_name$request_uri;
}
5. PHP Configuration
Upload Limits
The application uploads logos to /img with a 1MB limit (see ajax/imagen_ajax.php:19). Configure PHP accordingly:
; /etc/php/5.6/apache2/php.ini (or php-fpm/php.ini for Nginx)
upload_max_filesize = 2M
post_max_size = 3M
max_execution_time = 30
memory_limit = 128M
; File upload security
file_uploads = On
allow_url_fopen = Off
allow_url_include = Off
Logo uploads support JPG, JPEG, PNG, and GIF formats. The validation is at ajax/imagen_ajax.php:17.
Error Handling
Production settings:
; Display errors - DISABLED for production
display_errors = Off
display_startup_errors = Off
; Log errors instead
log_errors = On
error_log = /var/log/php/error.log
; Error reporting level
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
Create log directory:
mkdir -p /var/log/php
chown www-data:www-data /var/log/php
chmod 755 /var/log/php
6. Application Hardening
Disable Directory Listing
The application includes index.php files in most directories, but add this to your web server config:
Apache:
Nginx:
Protect Configuration Files
Apache - Create .htaccess in /config:
Order deny,allow
Deny from all
Nginx - Already configured in the example above:
location ~ /config/ {
deny all;
return 404;
}
Backup Strategies
Database Backups
Create backup script
#!/bin/bash
# /usr/local/bin/backup-invoice-db.sh
BACKUP_DIR="/var/backups/simple-invoice"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="simple_invoice"
DB_USER="invoice_user"
DB_PASS="your_password"
mkdir -p $BACKUP_DIR
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/db_$DATE.sql.gz
# Keep last 30 days
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +30 -delete
Schedule with cron
# Daily at 2 AM
0 2 * * * /usr/local/bin/backup-invoice-db.sh
Test restoration
# Restore from backup
gunzip < /var/backups/simple-invoice/db_20260305_020000.sql.gz | mysql -u invoice_user -p simple_invoice
File Backups
Backup uploaded files and PDFs:
#!/bin/bash
# /usr/local/bin/backup-invoice-files.sh
BACKUP_DIR="/var/backups/simple-invoice"
APP_DIR="/var/www/simple-invoice"
DATE=$(date +%Y%m%d_%H%M%S)
tar -czf $BACKUP_DIR/files_$DATE.tar.gz \
$APP_DIR/img \
$APP_DIR/pdf/documentos \
$APP_DIR/config
# Keep last 30 days
find $BACKUP_DIR -name "files_*.tar.gz" -mtime +30 -delete
PHP OpCache
Enable OpCache for better performance:
; /etc/php/5.6/apache2/conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
Database Optimization
-- Analyze tables
ANALYZE TABLE clientes, facturas, productos, detalle_factura;
-- Optimize tables
OPTIMIZE TABLE clientes, facturas, productos, detalle_factura;
-- Add indexes for common queries
CREATE INDEX idx_status ON clientes(status_cliente);
CREATE INDEX idx_date ON facturas(date_added);
Web Server Caching
Apache:
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript
</IfModule>
# Browser caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
Monitoring
Error Monitoring
Monitor PHP error logs:
# Real-time monitoring
tail -f /var/log/php/error.log
# Alert on errors
grep -i "fatal\|error" /var/log/php/error.log | mail -s "Invoice Errors" admin@yourdomain.com
Database Monitoring
-- Check database size
SELECT
table_name AS 'Table',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.TABLES
WHERE table_schema = 'simple_invoice'
ORDER BY (data_length + index_length) DESC;
-- Check connection count
SHOW STATUS WHERE Variable_name = 'Threads_connected';
Disk Space Monitoring
# Monitor upload directories
du -sh /var/www/simple-invoice/img
du -sh /var/www/simple-invoice/pdf/documentos
Update Procedures
Backup current installation
tar -czf simple-invoice-backup-$(date +%Y%m%d).tar.gz /var/www/simple-invoice
Enable maintenance mode
Create a temporary maintenance page or use web server redirect.
Update application files
cd /var/www/simple-invoice
git pull origin main
# Or extract new version archive
Preserve configuration
# Ensure config/db.php is not overwritten
git update-index --assume-unchanged config/db.php
Update database schema
mysql -u invoice_user -p simple_invoice < migrations/update.sql
Clear OpCache
service apache2 reload
# Or for PHP-FPM:
service php5.6-fpm reload
Test and restore access
Verify the application is working, then disable maintenance mode.
Troubleshooting
Database Connection Errors
Check config/conexion.php:8-14 for connection logic. Common issues:
# Test MySQL connection
mysql -u invoice_user -p -h localhost simple_invoice
# Check MySQL is running
systemctl status mysql
# Verify credentials match config/db.php
grep -E "DB_USER|DB_PASS|DB_NAME" config/db.php
Upload Failures
Logo upload validation at ajax/imagen_ajax.php:17-20:
# Check directory permissions
ls -la img/
# Verify PHP upload settings
php -i | grep -E "upload_max_filesize|post_max_size"
# Check web server error logs
tail -f /var/log/apache2/error.log # Apache
tail -f /var/log/nginx/error.log # Nginx
Permission Issues
# Fix ownership
chown -R www-data:www-data /var/www/simple-invoice
# Fix permissions
find /var/www/simple-invoice -type d -exec chmod 755 {} \;
find /var/www/simple-invoice -type f -exec chmod 644 {} \;
chmod 755 /var/www/simple-invoice/img
chmod 755 /var/www/simple-invoice/pdf/documentos
Security Checklist
Before going live:
Next Steps
- Set up monitoring and alerts
- Configure fail2ban for brute force protection
- Implement regular security audits
- Plan disaster recovery procedures
- Document your specific deployment configuration