{"id":1903,"date":"2025-04-08T16:37:45","date_gmt":"2025-04-08T13:37:45","guid":{"rendered":"https:\/\/upcloud.com\/global\/us\/resources\/tutorials\/setting-up-a-secure-ruby-on-rails-environment-on-upcloud-best-practices\/"},"modified":"2025-04-08T16:37:45","modified_gmt":"2025-04-08T13:37:45","slug":"setting-up-a-secure-ruby-on-rails-environment-on-upcloud-best-practices","status":"publish","type":"tutorial","link":"https:\/\/upcloud.com\/global\/resources\/tutorials\/setting-up-a-secure-ruby-on-rails-environment-on-upcloud-best-practices\/","title":{"rendered":"Setting Up a Secure Ruby on Rails Environment on UpCloud: Best Practices"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Ruby on Rails (RoR) is a robust, open-source web development framework designed to help developers build applications efficiently. Written in the <a href=\"https:\/\/www.ruby-lang.org\/en\/\" target=\"_blank\" rel=\"noopener\">Ruby programming language<\/a>, this framework provides a well-defined structure that organizes an application&#8217;s code, databases, and logic for seamless development. It follows the <a href=\"https:\/\/www.freecodecamp.org\/news\/the-model-view-controller-pattern-mvc-architecture-and-frameworks-explained\/\" target=\"_blank\" rel=\"noopener\">Model-View-Controller (MVC) architectural pattern<\/a>, which separates the business logic, user interface, and controller\u2014the component that acts as an intermediary between the&nbsp; user interface and business logic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Despite its structured architecture, built-in security features, and rapid development capabilities, securing Ruby on Rails applications is crucial to protect them from potential vulnerabilities and cyber threats. Applications may become vulnerable to exploitation, cyberattacks, and data breaches without proper security measures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this Tutorial, we&#8217;ll walk you through setting up a secure Ruby on Rails environment, while digging into the best security tricks for today&#8217;s web apps. From configuration and authentication to encryption and monitoring, you&#8217;ll pick up key skills to shield your Rails app from threats.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Understanding Ruby on Rails security&nbsp;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Security vulnerabilities remain a major concern across all web applications. Many applications are built without proper security assessments and best practices, making them easy targets for exploitation. Even those built with Ruby on Rails aren\u2019t immune to attacks if safeguards aren\u2019t in place.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thankfully, Rails provides built-in security features like CSRF protection, strong parameters, and automatic input sanitization to help defend against common threats. Let&#8217;s explore a few of the most critical threats and how Rails helps address them.&nbsp;<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">1. SQL Injection<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">SQL injection occurs when an attacker injects malicious SQL statements into a query, manipulating the database into executing unintended commands. For example, an improperly sanitized input field could allow an attacker to bypass authentication and gain access to sensitive user data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prevention in Rails:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use ActiveRecord&#8217;s parameterized queries, which automatically sanitize inputs: <code>User.where(\"email = ?\", params[:email])<\/code><\/li>\n\n\n\n<li>Avoid string interpolation in queries, as it can lead to vulnerabilities.<\/li>\n\n\n\n<li>Implement least privilege access for database users to limit damage in case of a breach.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>2. Cross-Site Scripting (XSS)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Cross-site scripting (XSS) is an attack where malicious scripts are injected into a webpage. When unsuspecting users visit the page, the injected script runs in their browser, making it possible for their credentials, session tokens, or other sensitive information to be stolen. Sometimes, attackers modify the page content to mislead users into taking unintended actions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prevention in Rails:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rails escapes user input by default, but always verify that your views do not render raw, unsanitized content.<\/li>\n\n\n\n<li>Use <strong>Rails helper methods<\/strong> like <strong>sanitize<\/strong> and <strong>h<\/strong> (HTML escape) to prevent script injection: <code>&lt;%= sanitize(user_input) %&gt;<\/code><\/li>\n\n\n\n<li>Set <strong>secure Content Security Policy (CSP) headers<\/strong> to restrict which scripts can execute in your application.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">3. Cross-Site Request Forgery (CSRF)<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">CSRF attacks trick users into performing unintended actions while authenticated in a web application. An attacker can embed a hidden malicious request into a seemingly harmless webpage, tricking the victim into unknowingly submitting a form, changing account settings, or even transferring funds. Since the user&#8217;s session authenticates the request, the server assumes it&#8217;s legitimate and processes it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prevention in Rails:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rails includes CSRF protection by default. Ensure you have the <code>protect_from_forgery<\/code> directive enabled in your controllers:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">class ApplicationController &lt; ActionController::Base\n&nbsp; protect_from_forgery with: :exception\nend<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use authenticity tokens in all form submissions to validate requests.<\/li>\n\n\n\n<li>Implement SameSite cookie attributes to prevent cookies from being sent in cross-site requests.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Now that you\u2019ve been exposed to the threats and security measures in Rails let\u2019s look at how to set up a secure Ruby on Rails environment on an UpCloud server.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Preparing the environment<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This section walks through setting up a secure Ruby on Rails environment on an <a href=\"https:\/\/upcloud.com\/global\/products\/cloud-servers\">UpCloud server<\/a> running Ubuntu 24.04 LTS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before we begin, ensure you have:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&nbsp;<a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/quick-start-guide\/\">UpCloud server<\/a> running Ubuntu 24.04 LTS<\/li>\n\n\n\n<li>&nbsp;SSH access with root privileges to the UpCloud server<\/li>\n\n\n\n<li><a href=\"https:\/\/upcloud.com\/global\/docs\/products\/managed-postgresql\/\">UpCloud PostgreSQL<\/a> Database running PostgreSQL 17.4<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Installation and Setup<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 1: Connect to Your Server<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, SSH into your UpCloud server as root:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ssh root@your-upcloud-ip<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 2: Update and Secure the System<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Update packages and reboot to apply changes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">apt update &amp;&amp; apt upgrade -y\nreboot  # Reconnect after reboot<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 3: Create a Secure Non-Root User.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Using a non-root user enhances security. Create a user <code>ruby<\/code>, grant it sudo access, and switch to it. <em>Why? Running applications as root is a security risk. This ensures Rails runs with limited privileges.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo useradd -m -s \/bin\/bash -G sudo ruby &amp;&amp; echo \"ruby:your-preferred-password\" | sudo chpasswd\nsu - ruby<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 4: Install Ruby 3.4.2 with rbenv<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Installing the latest stable version of Ruby, like 3.4.2, is a best practice for security and performance. Newer versions include critical security patches, improved efficiency, and compatibility with the <a href=\"https:\/\/rubyonrails.org\/2025\/3\/12\/Rails-Version-8-0-2-has-been-released\" target=\"_blank\" rel=\"noopener\">latest Rails version (8.0.2)<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4.1. Install dependencies:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt install -y git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libncurses5-dev libffi-dev libgdbm-dev<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">4.2. Set up <code>rbenv<\/code> to manage Ruby versions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">git clone https:\/\/github.com\/rbenv\/rbenv.git ~\/.rbenv\necho 'export PATH=\"$HOME\/.rbenv\/bin:$PATH\"' &gt;&gt; ~\/.bashrc\necho 'eval \"$(rbenv init -)\"' &gt;&gt; ~\/.bashrc\nsource ~\/.bashrc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">4.3. Install <strong><code>ruby-build <\/code><\/strong>plugin and Ruby 3.4.2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">git clone https:\/\/github.com\/rbenv\/ruby-build.git ~\/.rbenv\/plugins\/ruby-build\nrbenv install 3.4.2\nrbenv global 3.4.2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 5: Install Node.js and Yarn<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails requires JavaScript runtime support, so install Node.js 20.x and Yarn using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">curl -sL https:\/\/deb.nodesource.com\/setup_20.x | sudo bash -\nsudo apt install -y nodejs\nsudo npm install -g yarn<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 6: Configure Your UpCloud PostgreSQL Database<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">UpCloud offers two secure connection options for accessing your managed PostgreSQL database:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Private connection via the Utility network (recommended)<\/li>\n\n\n\n<li>Public internet access with IP allowlisting and SSL<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Choose the approach that fits your deployment environment. We&#8217;ll cover both.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6.1 Option 1: Secure with UpCloud\u2019s Utility Network (Private Connection)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From your <strong>UpCloud DBaaS Overview<\/strong> page:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Enable<\/strong>: Automatic access from Utility network servers.<\/li>\n\n\n\n<li><strong>Disable<\/strong>: Allow access from all IPs.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-134-1024x326.png\" alt=\"-\" class=\"wp-image-49619\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This setup allows only servers inside your UpCloud account and within the same zone to communicate with the database over a private internal network. Traffic stays inside UpCloud\u2019s internal infrastructure\u2014safer and faster than routing over the public internet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6.2 Option 2: Secure Public Access (if not using Utility Network)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re not using private networking, and instead want to connect over the public internet:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Enable<\/strong>: Public connection in your UpCloud DB instance settings<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-136-1024x316.png\" alt=\"-\" class=\"wp-image-49621\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Disable: Automatic access from Utility network server<\/li>\n\n\n\n<li>Disable: Allow access from all IPs<\/li>\n\n\n\n<li>Add: Your Rails server&#8217;s public IP under Allowed IPs<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-135-1024x404.png\" alt=\"-\" class=\"wp-image-49620\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This method requires extra care. Always enforce SSL and limit access to trusted IPs only.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6.3: Take Note of Your DB Credentials<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Still in the <strong>Overview section<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Go to the <strong>Private connection<\/strong> section (or <strong>Public connection<\/strong>, based on your setup)<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Note the <strong>host<\/strong> and <strong>port<\/strong> values<\/li>\n\n\n\n<li>Avoid using read-only or replica hosts \u2014 only use the primary connection<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-137-1024x342.png\" alt=\"-\" class=\"wp-image-49622\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Scroll to the <strong>Auth<\/strong> section<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Take note of your <strong>username<\/strong> and <strong>password<\/strong><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-138-1024x385.png\" alt=\"-\" class=\"wp-image-49623\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Tip: You can also create your own database user instead of using the default.Go to the <strong>Users<\/strong> tab \u2192 click <strong>Create User<\/strong> \u2192 enter your desired <strong>username and password<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-139-1024x401.png\" alt=\"-\" class=\"wp-image-49624\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This lets you set up custom access control for different environments (e.g., development, staging, production).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6.4 Create Separate Databases for Development and Test<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the <strong>Databases<\/strong> tab:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Click <strong>Create Database<\/strong><\/li>\n\n\n\n<li><strong>Create two databases named:<\/strong>\n<ul class=\"wp-block-list\">\n<li>secure_blog_development<\/li>\n\n\n\n<li>secure_blog_test<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This allows Rails to manage separate environments securely and cleanly.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/image-140-1024x445.png\" alt=\"-\" class=\"wp-image-49625\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">6.5 Install PostgreSQL Development Libraries<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Install the necessary system packages for Rails to connect with PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt install -y libpq-dev<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These libraries are required for the pg gem to compile and work correctly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 7:&nbsp; Install Rails and Create a New Application<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7.1 Run the command below to install the latest stable version of Rails:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem install rails<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">7.2. Create a new Rails application with PostgreSQL as the database:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">rails new secure_blog --database=postgresql\ncd secure_blog<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 8: Configure Rails Database Credentials<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8.1. Edit the <code>database.yml <\/code>file to match PostgreSQL credentials:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">nano config\/database.yml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">8.2. replace <strong>development<\/strong> and <strong>test<\/strong> sections with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">development:\n  adapter: postgresql\n  database: secure_blog_development\n   username: &lt;%= ENV['DB_USERNAME'] %&gt;\n  password: &lt;%= ENV['DB_PASSWORD'] %&gt;\n  host: &lt;%= ENV['DB_HOST'] %&gt;\n  port: &lt;%= ENV['DB_PORT']  %&gt;\n\ntest:\n  adapter: postgresql\n  database: secure_blog_test\n  username: &lt;%= ENV['DB_USERNAME'] %&gt;\n  password: &lt;%= ENV['DB_PASSWORD'] %&gt;\n  host: &lt;%= ENV['DB_HOST'] %&gt;\n  port: &lt;%= ENV['DB_PORT']  %&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Click on ctrl x, y, and enter to save.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Note: Hardcoding credentials in config files can be a security risk. Instead, use environment variables:<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8.3.&nbsp; Set these in the terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">export DB_USERNAME=your_upcloud_DB_username\nexport DB_PASSWORD=your_upcloud_DB_password\nexport DB_HOST=your_upcloud_DB_hostname  # Use private or public hostname\nexport DB_PORT=your_upcloud_DB_port<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 9: Initialize Database and Start Rails Server<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run the following commands to set up the database and start the Rails app:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">rails db:migrate\nrails server --binding=0.0.0.0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Your Rails app should now be accessible at: <code>http:\/\/your-server-ip:3000<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the application successfully running, let\u2019s take a look at some best practices for securing your Rails application, covering authentication, encryption, and vulnerability protection.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Securing the rails configuration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Securing a Ruby on Rails application starts at the configuration level. Misconfigured settings can expose sensitive data, create vulnerabilities, or allow attackers to manipulate the system. Let&#8217;s explore key measures to fortify your Rails application\u2019s configuration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Environment Configuration Files<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">As seen in Step 8.2, we avoided hardcoding database credentials in database.yml. While hardcoding these values may not pose an immediate risk in a local development environment, it is a critical vulnerability in production. Exposing this file in a .git repository automatically leaks sensitive data, increasing the risk of unauthorized access.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Rails&#8217; Encrypted Credentials: A Secure Alternative<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">To enhance security, Rails replaced <code>secrets.yml<\/code> with <code>config\/credentials.yml.enc<\/code>, encrypting secrets by default. Instead of manually setting credentials via export commands, store them in<strong> <\/strong><code>credentials.yml.enc<\/code> for added security.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run the following command to edit encrypted credentials:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">EDITOR=nano rails credentials:edit<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add your sensitive data inside:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">secret_key_base: your_random_key_here\ndb_password: securepassword<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, reference it securely in<code> database.yml:<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">production:\n  adapter: postgresql\n  database: secure_blog_production\n  username: blog_user\n  password: &lt;%= Rails.application.credentials.db_password %&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The<code> config\/credentials.yml.enc<\/code> file, even when committed to a Git repository, remains secure because it is encrypted. It can only be decrypted using the <code>config\/master.key<\/code> file, which is never committed to Git and should always be stored securely.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Strong Parameters <\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Unrestricted user input is one of the most overlooked security risks in web applications. Attackers can exploit input vulnerabilities to modify data, escalate privileges, or inject malicious code. Rails enforce Strong Parameters to mitigate this risk, preventing mass assignment vulnerabilities.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Understanding Strong Parameters<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/learn.snyk.io\/lesson\/mass-assignment\/?ecosystem=javascript\" target=\"_blank\" rel=\"noopener\">In 2012, GitHub suffered a breach when an attacker injected unauthorized parameters, allowing them to modify user account settings<\/a>. This happened because version 3 of Rails, which was used then, did not restrict which parameters could be updated; this allowed attackers to manipulate data at will.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails 4 to 8.0.2 prevents this by enforcing strong parameters:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"># app\/controllers\/posts_controller.rb\nclass PostsController &lt; ApplicationController\n  def create\n    @post = Post.new(post_params)\n    if @post.save\n      redirect_to @post, notice: \"Post created!\"\n    else\n      render :new\n    end\n  end\n\n  private\n\n  def post_params\n    params.require(:post).permit(:title, :content)\n  end\nend<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><strong>require(:post):<\/strong> <\/code>Ensures a post key exists in params.<\/li>\n\n\n\n<li><code><strong>permit(:title, :content)<\/strong>:<\/code> Explicitly allows only title and content.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Without strong parameters, an attacker could send:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">{\n  \"post\": {\n    \"title\": \"Hacked Post\",\n    \"is_admin\": true\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If Rails didn\u2019t enforce parameter whitelisting, this could grant admin privileges, manipulate account settings, or alter sensitive fields.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails 8.0.2 strictly enforces mass-assignment security, ensuring that unpermitted attributes raise an error instead of silently overriding database records.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Whitelisting Parameters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A common mistake in web security is relying on blacklisting (e.g., blocking specific fields like admin). However, blacklists fail when new fields are introduced, allowing attackers to bypass restrictions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead, Rails enforces a whitelisting approach, processing only explicitly permitted fields.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">def user_params\n  params.require(:user).permit(:name, :email, :password, :password_confirmation)\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If an attacker attempts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">{\n  \"user\": {\n    \"name\": \"Attacker\",\n    \"admin\": true\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Rails will automatically discard the admin field, preventing unauthorized privilege escalation.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Handling Nested Parameters Safely<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">For complex forms, such as posts with tags, Rails allows structured whitelisting to prevent arbitrary nested input.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">def post_params\n  params.require(:post).permit(:title, :content, tags: [:name])\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This ensures that only title, content, and tags[:name] are accepted\u2014anything else is ignored.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Authentication and Authorization best practices.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Authentication verifies who users are, while authorization dictates what they can do. Getting these right is non-negotiable for a secure Rails app. Proper systems and tools must be in place to ensure applications adhere to industry security standards. Techniques like Role-Based Access Control (RBAC) and Multi-Factor Authentication (MFA) are crucial in securing modern applications. Let\u2019s explore some.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Secure User Authentication<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Authentication is used to confirm user identities. Ruby provides reusable authentication packages (Ruby gems) to streamline this process. Popular options include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Devise<\/strong> \u2013 A full-featured authentication system.<\/li>\n\n\n\n<li><strong>Pundit<\/strong> \u2013 A policy-based authorization library.<\/li>\n\n\n\n<li><strong>Sorcery<\/strong> \u2013 A lightweight authentication alternative.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Using Devise for Authentication<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Devise is one of the most popular authentication solutions for Rails. It provides an out-of-the-box system for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>User sign-up, login, and logout<\/li>\n\n\n\n<li>Password recovery and account confirmation<\/li>\n\n\n\n<li>Session management and authentication security<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To integrate Devise into your Rails application:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">cd ~\/secure_blog\nnano Gemfile<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add Devise to the Gemfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem 'devise'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then install the gem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">bundle install\nrails generate devise:install<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This automates authentication, saving developers time while ensuring secure user handling.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Enhancing Password Security with bcrypt<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Basic password hashing is not enough to prevent brute-force attacks. <strong>Bcrypt<\/strong> enhances security by:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Adding a piece of data referred to as &#8220;salt&#8221; to the hash makes password hashes unique and resistant to rainbow table attacks.<\/li>\n\n\n\n<li>Slowing down brute-force attempts by using computationally expensive hashing.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Devise already depends on bcrypt, so no additional setup is required when using it. However, if manually implementing authentication, install bcrypt separately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem 'bcrypt', '~&gt; 3.1.7'<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Role-Based Access Control (RBAC)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">While authentication ensures users can log in, authorization determines what they can access. Implementing RBAC prevents unauthorized actions by restricting user roles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A least-privilege system ensures users only have access to what they need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>User \u2013 Can view posts only.<\/li>\n\n\n\n<li>Editor \u2013 Can view and write posts but not delete them.<\/li>\n\n\n\n<li>Admin \u2013 Has full privileges (view, write, delete).<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Policy Object Pattern for RBAC in Rails<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Using Policy Objects, we can define rules, conditions, and logic to manage user permissions in Rails.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example: Implementing a Policy Object in Rails:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"># app\/policies\/post_policy.rb\nclass PostPolicy\n  attr_reader :user, :post\n\n  def initialize(user, post)\n    @user = user\n    @post = post\n  end\n\n  # Can the user view the post?\n  def show?\n    true # All roles can view posts\n  end\n\n  # Can the user create a post?\n  def create?\n    user.editor? || user.admin? # Editors and admins can create posts\n  end\n\n  # Can the user edit a post?\n  def update?\n    (user.editor? &amp;&amp; post.author == user) || user.admin? # Editors can edit their posts; admins can edit any post\n  end\n\n  # Can the user delete a post?\n  def destroy?\n    user.admin? # Only admins can delete posts\n  end\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This decouples business logic from controllers, keeping authorization clean and scalable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Two-Factor Authentication (2FA) for Enhanced Security<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Credential leaks and phishing attacks can still compromise accounts, even with strong passwords. 2FA adds an extra security layer, requiring users to verify their identity with an additional authentication factor (e.g., a one-time password (OTP) or authenticator app).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To integrate 2FA with Devise, use the devise-two-factor gem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem 'devise-two-factor'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, configure it within your User model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">class User &lt; ApplicationRecord\n  devise :two_factor_authenticatable\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Note: You\u2019ll need a database tweak\u2014like an <\/em><code><strong>otp_secret column<\/strong> <\/code><em>and some setup for OTP generation<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Encryption and Data Security<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Encryption is essential for securing applications and websites, whether data is in transit or at rest. HTTPS is non-negotiable in modern workloads as it encrypts communication between a user\u2019s browser and the server, protecting sensitive data from interception.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails provides the <code>force_ssl<\/code> configuration to ensure the applications always run under HTTPS.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To enforce encryption in production environments, enable the<code> force_ssl <\/code>flag in <code>config\/environments\/production.rb:<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">config.force_ssl = true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To enforce HTTPS across all environments, enable<strong> <\/strong><code>force_ssl<\/code> in<strong> <\/strong><code>config\/application.rb<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">config.force_ssl = true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">However, enforcing HTTPS at the application level is not enough\u2014the server itself must be configured correctly. For deployments within UpCloud servers, this involves:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Installing Nginx to handle incoming traffic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt install nginx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Setting up SSL certificates with Let\u2019s Encrypt (Certbot):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt install certbot python3-certbot-nginx\nsudo certbot --nginx -d yourdomain.com -d www.yourdomain.com<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Configuring Nginx to redirect HTTP to HTTPS and enable SSL\/TLS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">erver {\n    listen 80;\n    server_name yourdomain.com www.yourdomain.com;\n    return 301 https:\/\/$host$request_uri;\n}\n\nserver {\n    listen 443 ssl;\n    server_name yourdomain.com www.yourdomain.com;\n\n    ssl_certificate \/etc\/letsencrypt\/live\/yourdomain.com\/fullchain.pem;\n    ssl_certificate_key \/etc\/letsencrypt\/live\/yourdomain.com\/privkey.pem;\n\n    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_prefer_server_ciphers on;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Encryption in Transit<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Data transmitted between a client (browser, API, or external service) and a server must be encrypted in transit to prevent man-in-the-middle (MITM) attacks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">HTTPS uses TLS (Transport Layer Security) to encrypt all communication, ensuring:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Confidentiality<\/strong> \u2013 Prevents attackers from reading sensitive data.<\/li>\n\n\n\n<li><strong>Integrity<\/strong> \u2013 Protects against data tampering during transmission.<\/li>\n\n\n\n<li><strong>Authentication<\/strong> \u2013 Ensures the server is legitimate and not an imposter.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Encryption at Rest<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">For data stored in the database, application-level encryption is crucial. Rails provides <strong>Active Record Encryption<\/strong>, allowing developers to declare specific attributes to be encrypted. It means that;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The application reads unencrypted data, but the database stores it in an encrypted format.<\/li>\n\n\n\n<li>Data is encrypted &amp; decrypted automatically when accessed.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example of encrypting fields in a Rails model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">class User &lt; ApplicationRecord\n  encrypts :email, :phone_number\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With this setup:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The database stores email and phone number in an encrypted form.<\/li>\n\n\n\n<li>The Rails app decrypts them when accessed but prevents unauthorized access in case of a database breach.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Securing API Keys<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Hardcoding API keys poses a serious security risk, especially if the codebase is exposed publicly. API keys must be stored securely to prevent unauthorized use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One approach is using environment variables with the <code>dotenv-rails<\/code> gem:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Install <code>dotenv-rails<\/code> by adding it to the Gemfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem 'dotenv-rails'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a <code>.env<\/code> in the root directory and store your variables<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">API_KEY=up15281cloud<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Access the key inside your Rails app:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ENV['API_KEY']<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Always add <code>.env<\/code> to <code>.gitignore<\/code> to prevent secret leaks in version control:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring and Logging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After setting up an application, proper logging and monitoring are essential for ensuring its health and smooth operation. Developers rely on logs to pinpoint issues, troubleshoot errors, and track security events. Fortunately, Rails has built-in logging and monitoring capabilities that can be enhanced using gems like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lograge<\/strong> \u2013 which formats logs in a more structured and readable manner.<\/li>\n\n\n\n<li><strong>New Relic\u2019s Ruby APM<\/strong> \u2013 which tracks response times, database queries, and crashes.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;Rails allows custom event logging using <strong><code>ActiveSupport::Notifications:<\/code><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Rails.logger.info \"Suspicious login attempt from IP: #{request.remote_ip}\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Hosting Rails on an UpCloud cloud server provides real-time infrastructure monitoring, tracking:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CPU usage<\/strong> \u2013 Detects excessive resource consumption.<\/li>\n\n\n\n<li><strong>Memory &amp; disk usage<\/strong> \u2013 Flags potential bottlenecks.<\/li>\n\n\n\n<li>&nbsp;<strong>Network traffic<\/strong> \u2013 Helps identify unusual spikes or potential attacks.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Using these tools security threats can be spotted, watch out for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Spikes in 404s<\/strong> \u2013 Could indicate probing for vulnerabilities.<\/li>\n\n\n\n<li><strong>Repeated failed logins<\/strong> \u2013 A potential brute-force attack.<\/li>\n\n\n\n<li><strong>Unusual traffic surges<\/strong> \u2013 UpCloud\u2019s dashboard can flag suspicious activity.<br><\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Performance Optimization and Scalability<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A slow application leads to poor user experience, high bounce rates, and lost revenue. Users are more likely to switch to an alternative if a competing app loads even a few seconds faster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Building a Ruby on Rails application on UpCloud\u2019s high-performance infrastructure is a great start, but proper optimization techniques ensure:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Better user experience (faster load times).<\/li>\n\n\n\n<li>Lower latency (quicker responses to user actions).<\/li>\n\n\n\n<li>Scalability (ability to handle increasing traffic).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Two essential techniques for performance and scalability are caching and CDN integration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Caching<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Caching stores frequently accessed data in memory to reduce server load and improve response times. Rails have built-in caching mechanisms, which includes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fragment Caching &#8211; <\/strong>Caches parts of a page (e.g., a frequently displayed sidebar, comments section, or navigation bar). It is used to avoid regenerating content that doesn\u2019t change often.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;% cache @article do %&gt;\n  &lt;%= render @article %&gt;\n&lt;% end %&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If this article was loaded before, Rails fetches it from cache instead of reloading it from the database.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Database Query Caching &#8211; <\/strong>Rails remembers the result of a database query during a request cycle to avoid redundant lookups.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ActiveRecord::Base.cache do\n  Post.find(1)  # Runs a database query\n  Post.find(1)  # Retrieves result from cache, no new query\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The second query <strong>fetches the result from memory instead of hitting the database<\/strong>, reducing load time.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">How UpCloud Enhances Caching Performance<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/upcloud.com\/global\/products\/block-storage\">High-performance SSD storage<\/a> reduces database read\/write times.<\/li>\n\n\n\n<li>Memory-optimized compute instances help with caching-heavy applications.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Content Delivery Network (CDN)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Content Delivery Network (CDN) improves application performance by caching and serving content from servers closer to users. This reduces:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Load on the origin server.<\/li>\n\n\n\n<li>Latency by serving static files faster.<\/li>\n\n\n\n<li>Bandwidth usage, optimizing performance at scale.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Securing a Ruby on Rails application goes beyond just writing clean code\u2014it requires a proactive approach to security, performance, and scalability. You can avoid vulnerabilities and protect your workload from threats by adhering to best practices like configuring secure environment files, enforcing HTTPS, implementing strong authentication, encrypting sensitive data, and monitoring logs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Plus, you can ensure your application remains fast and scalable by using performance optimizations like caching, CDNS, and <a href=\"https:\/\/upcloud.com\/global\/products\/cloud-servers\">UpCloud&#8217;s high-performance servers<\/a> and infrastructures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, security is a continuous task. Regularly updating dependencies, applying security patches, and monitoring security advisories are crucial to staying immune to potent risk. Building a secure and scalable Rails application is about continuous improvement\u2014stay informed, implement best practices, and keep your application resilient and future-ready.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"author":83,"featured_media":49644,"comment_status":"open","ping_status":"closed","template":"","community-category":[253],"class_list":["post-1903","tutorial","type-tutorial","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/tutorial"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/83"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=1903"}],"version-history":[{"count":0,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1903\/revisions"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=1903"}],"wp:term":[{"taxonomy":"community-category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/community-category?post=1903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}